package cn.jbolt.core.service.base;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.jbolt.core.base.JBoltIDGenMode;
import cn.jbolt.core.base.JBoltMsg;
import cn.jbolt.core.base.TreeCheckIsParentNode;
import cn.jbolt.core.base.config.JBoltConfig;
import cn.jbolt.core.bean.JsTreeBean;
import cn.jbolt.core.bean.JsTreeStateBean;
import cn.jbolt.core.bean.Option;
import cn.jbolt.core.bean.OptionBean;
import cn.jbolt.core.consts.JBoltConst;
import cn.jbolt.core.db.sql.DBType;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.model.base.JBoltBaseModel;
import cn.jbolt.core.model.base.JBoltModelConfig;
import cn.jbolt.core.para.jbolttablemenufilter.JBoltTableMenuFilter;
import cn.jbolt.core.util.JBoltArrayUtil;
import cn.jbolt.core.util.JBoltCamelCaseUtil;
import cn.jbolt.core.util.JBoltListMap;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Okv;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.*;
import com.jfinal.plugin.activerecord.Record;

import java.util.*;


/**
 * JBolt提供Service层的基础底层封装 灵活 易用
 *
 * @ClassName: BaseService
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2019年11月14日
 */
public abstract class JBoltBaseService<M extends JBoltBaseModel<M>> extends JBoltCommonService {
	/**
	 * 获取Sql模板 model模式
	 *
	 * @param key
	 * @return
	 */
	public DaoTemplate<M> daoTemplate(String key) {
		return daoTemplate(key, Okv.create());
	}

	/**
	 * 获取Sql模板 model模式
	 *
	 * @param key
	 * @param data
	 * @return
	 */
	public DaoTemplate<M> daoTemplate(String key, Kv data) {
		return daoTemplate(key, Okv.create().set(data));
	}

	/**
	 * 获取Sql模板 model模式
	 *
	 * @param key
	 * @param data
	 * @return
	 */
	public DaoTemplate<M> daoTemplate(String key, Okv data) {
		data.setIfNotBlank(SQL_TPL_KEY_TABLE, JBoltModelConfig.me.processTableName(dao().getClass()));
		data.setIfNotBlank(SQL_TPL_KEY_PRIMARYKEY, primaryKey());
		return dao().template(key, data);
	}

	/**
	 * 获取表名
	 * @return
	 */
	public String table() {
		return dao()._getTableName();
	}

	@Override
	public String dataSourceConfigName() {
		return dao()._getDataSourceConfigName();
	}

	@Override
	protected String database() {
		if (notOk(databaseName)) {
			databaseName = dao()._getDatabaseName();
		}
		return databaseName;
	}

	@Override
	protected String schema() {
		if (notOk(schema)) {
			schema = dao()._getSchema();
			if(notOk(schema) && DBType.isSqlServer(dbType())){
				schema = JBoltConst.JBOLT_SQLSERVER_SCHEMA_DEFAULT;
			}
		}
		return schema;
	}

	@Override
	public String dbType() {
		if (notOk(dbType)) {
			dbType = dao()._getDbType();
			if(notOk(dbType)){
				dbType = DBType.MYSQL;
			}
		}
		return dbType;
	}

	@Override
	public String primaryKey() {
		if (notOk(primaryKey)) {
			primaryKey = dao()._getPrimaryKey();
			if(notOk(primaryKey)){
				primaryKey = ID;
			}
		}
		return primaryKey;
	}

	@Override
	public String idGenMode() {
		if (notOk(idGenMode)) {
			idGenMode = dao()._getIdGenMode();
			if(notOk(idGenMode)){
				idGenMode = JBoltIDGenMode.SNOWFLAKE;
			}
		}
		return idGenMode;
	}

	/**
	 * 获取Sql模板 Db模式
	 *
	 * @param key
	 * @return
	 */
	public DbTemplate dbTemplate(String key) {
		return dbTemplate(key, Okv.create());
	}

	/**
	 * 获取Sql模板 Db模式
	 *
	 * @param key
	 * @param data
	 * @return
	 */
	public DbTemplate dbTemplate(String key, Kv data) {
		return dbTemplate(key, Okv.create().set(data));
	}

	/**
	 * 获取Sql模板 Db模式
	 *
	 * @param key
	 * @param data
	 * @return
	 */
	public DbTemplate dbTemplate(String key, Okv data) {
		data.setIfNotBlank(SQL_TPL_KEY_TABLE, JBoltModelConfig.me.processTableName(dao().getClass()));
		data.setIfNotBlank(SQL_TPL_KEY_PRIMARYKEY, primaryKey());
		return Db.use(dataSourceConfigName()).template(key, data);
	}

	/**
	 * 得到下拉列表数据
	 *
	 * @param textColumn
	 * @param valueColumn
	 * @param paras
	 * @return
	 */
	public List<Option> getOptionList(String textColumn, String valueColumn, Okv paras) {
		if (dao().hasColumn(SORT_RANK) || dao().hasColumn(SORT_RANK.toUpperCase())) {
			return getOptionList(textColumn, valueColumn, paras, SORT_RANK, null);
		}
		return getOptionList(textColumn, valueColumn, paras, null, null);
	}

	/**
	 * 按照指定字段和排序方式返回options
	 *
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<Option> getOptionListOrderBy(String orderColumns, String orderTypes) {
		return getOptionList(NAME, ID, null, orderColumns, orderTypes);
	}

	/**
	 * 得到下拉列表数据
	 *
	 * @param textColumn   哪个字段作为显示的text
	 * @param valueColumn  哪个字段作为选中的值
	 * @param paras        查询条件
	 * @param orderColumns 排序字段 可多个
	 * @param orderTypes   排序方式 可多个
	 * @return
	 */
	public List<Option> getOptionList(String textColumn, String valueColumn, Okv paras, String orderColumns,
			String orderTypes) {
		Okv conf = Okv.by(VALUE, valueColumn).set(TEXT, textColumn).set(SQL_TPL_KEY_MYPARAS, paras)
				.set(SQL_TPL_KEY_CUSTOM_COMPARE, false);
		if (isOk(orderColumns)) {
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, JBoltArrayUtil.from(orderColumns, KEY_COMMA));
			if (notOk(orderTypes)) {
				orderTypes = Sql.KEY_ASC_TRIMED;
			}
			conf.set(SQL_TPL_KEY_ORDER_TYPES, JBoltArrayUtil.from(orderTypes, KEY_COMMA));
		}
		List<Record> records = dbTemplate(SQL_TPL_COMMON_OPTIONLIST, conf).find();
		return toOptions(records);
	}

	/**
	 * 转为options
	 *
	 * @param records
	 * @return
	 */
	public List<Option> toOptions(List<Record> records) {
		return toOptions(records, "text", "value");
	}

	/**
	 * 转为options
	 *
	 * @param records
	 * @param textColumn
	 * @param valueColumn
	 * @return
	 */
	public List<Option> toOptions(List<Record> records, String textColumn, String valueColumn) {
		List<Option> options = new ArrayList<Option>();
		records.forEach(record -> options.add(new OptionBean(record.get(textColumn), record.get(valueColumn))));
		return options;
	}

	/**
	 * 得到下拉列表数据
	 *
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @return
	 */
	public List<Option> getOptionList(String textColumn, String valueColumn) {
		return getOptionList(textColumn, valueColumn, null);
	}
	/**
	 * 得到下拉列表数据 enable=true
	 * @return
	 */
	public List<Option> getOptionListEnable(){
		return getOptionListEnable(NAME, ID,true);
	}
	/**
	 * 得到下拉列表数据 is_deleted=false
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @return
	 */
	public List<Option> getOptionListIsDeletedFalse(String textColumn, String valueColumn){
		return getOptionListIsDeleted(textColumn, valueColumn,false);
	}
	/**
	 * 得到下拉列表数据 is_deleted=true
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @return
	 */
	public List<Option> getOptionListIsDeleted(String textColumn, String valueColumn){
		return getOptionListIsDeleted(textColumn, valueColumn,true);
	}
	/**
	 * 得到下拉列表数据  is_deleted=?
	 *
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @param isDeleted
	 * @return
	 */
	public List<Option> getOptionListIsDeleted(String textColumn, String valueColumn, Boolean isDeleted) {
		if(!dao().hasColumn("is_deleted")){
			throw new RuntimeException("table not has columns:is_deleted");
		}
		return getOptionList(textColumn, valueColumn,Okv.create().setIfNotNull("is_deleted",isDeleted));
	}
	/**
	 * 得到下拉列表数据 enable=true
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @return
	 */
	public List<Option> getOptionListEnable(String textColumn, String valueColumn){
		return getOptionListEnable(textColumn, valueColumn,true);
	}
	/**
	 * 得到下拉列表数据 enable=?
	 *
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @param enable
	 * @return
	 */
	public List<Option> getOptionListEnable(String textColumn, String valueColumn, Boolean enable) {
		if(!dao().hasColumn("enable")){
			throw new RuntimeException("table not has columns:enable");
		}
		return getOptionList(textColumn, valueColumn,Okv.create().setIfNotNull("enable",enable));
	}
	/**
	 * 得到下拉列表数据
	 *
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @param enable
	 * @param isDeleted
	 * @return
	 */
	public List<Option> getOptionList(String textColumn, String valueColumn, Boolean enable,Boolean isDeleted) {
		if(!dao().hasColumnAll("enable","is_deleted")){
			throw new RuntimeException("table not has columns:enable,is_deleted");
		}
		if(hasOk(enable,isDeleted)){
			Okv okv = Okv.create().setIfNotNull("enable",enable).setIfNotNull("is_deleted",isDeleted);
			return getOptionList(textColumn, valueColumn,okv);
		}
		return getOptionList(textColumn, valueColumn, null);
	}
	/**
	 * 得到下拉列表数据
	 *
	 * @return
	 */
	public List<Option> getOptionList() {
		return getOptionList(NAME, ID);
	}

	/**
	 * 得到下拉列表数据
	 * @param enable
	 * @param isDeleted
	 * @return
	 */
	public List<Option> getOptionList(Boolean enable,Boolean isDeleted) {
		if(!dao().hasColumnAll("enable","is_deleted")){
			throw new RuntimeException("table not has columns:enable,is_deleted");
		}
		if(hasOk(enable,isDeleted)){
			Okv okv = Okv.create().setIfNotNull("enable",enable).setIfNotNull("is_deleted",isDeleted);
			return getOptionList(NAME, ID,okv);
		}
		return getOptionList(NAME, ID);
	}

	/**
	 * 得到所有数据
	 *
	 * @return
	 */
	public List<M> findAll() {
		Sql sql = selectSql();
		if(dao().hasColumn(SORT_RANK) || dao().hasColumn(SORT_RANK.toUpperCase())){
			sql.orderBySortRank();
		}else{
			sql.orderByIdAscIfPgSql();
		}
		return find(sql);
	}

	/**
	 * 得到所有数据
	 * @param enable
	 * @param isDeleted
	 * @return
	 */
	public List<M> findAll(Boolean enable,Boolean isDeleted) {
		if(!dao().hasColumnAll("enable","is_deleted")){
			throw new RuntimeException("table not has columns:enable,is_deleted");
		}
		Sql sql = selectSql().enableEq(enable).isDeletedEq(isDeleted);
		if(dao().hasColumn(SORT_RANK) || dao().hasColumn(SORT_RANK.toUpperCase())){
			sql.orderBySortRank();
		}else{
			sql.orderByIdAscIfPgSql();
		}
		return find(sql);
	}

	/**
	 * 查询所有enable=true的
	 * 必须有enable字段
	 * @return
	 */
	public List<M> findAllEnable() {
		return findAllEnable(true);
	}
	/**
	 * 得到所有数据 enable=?
	 * 必须有enable字段
	 * @return
	 */
	public List<M> findAllEnable(boolean enable) {
		if(!dao().hasColumn("enable")){
			throw new RuntimeException("table not has column:enable");
		}
		Sql sql = selectSql().enableEq(enable);
		if(dao().hasColumn(SORT_RANK)){
			sql.orderBySortRank();
		}else{
			sql.orderByIdAscIfPgSql();
		}
		return find(sql);
	}

	/**
	 * 查询所有 is_deleted=true的
	 * 必须有is_deleted字段
	 * @return
	 */
	public List<M> findAllIsDeleted() {
		return findAllIsDeleted(true);
	}
	/**
	 * 查询所有 is_deleted=false的
	 * 必须有is_deleted字段
	 * @return
	 */
	public List<M> findAllIsDeletedFalse() {
		return findAllIsDeleted(false);
	}

	/**
	 * 得到所有数据 is_deleted=?
	 * 必须有is_deleted字段
	 * @return
	 */
	public List<M> findAllIsDeleted(boolean isDeleted) {
		if(!dao().hasColumn("is_deleted")){
			throw new RuntimeException("table not has column:is_deleted");
		}
		Sql sql = selectSql().isDeletedEq(isDeleted);
		if(dao().hasColumn(SORT_RANK)){
			sql.orderBySortRank();
		}else{
			sql.orderByIdAscIfPgSql();
		}
		return find(sql);
	}

	/**
	 * 抽象方法定义 dao 让调用者自己实现
	 *
	 * @return
	 */
	protected abstract M dao();

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 自定义参数compare
	 *
	 * @param columns       返回字段 多个逗号隔开
	 * @param paras         查询条件
	 * @param customCompare 是否自定义比较符
	 * @return
	 */
	public List<M> getCommonList(String columns, Okv paras, boolean customCompare) {
		return daoTemplate(SQL_TPL_COMMON_LIST, Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare)
				.setIfNotNull(SQL_TPL_KEY_MYPARAS, paras).setIfNotBlank(SQL_TPL_KEY_COLUMNS, columns)).find();
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 可以自定义参数compare 默认=
	 *
	 * @param paras         查询条件
	 * @param customCompare 是否自定义比较符
	 * @return
	 */
	public List<M> getCommonList(Okv paras, boolean customCompare) {
		return getCommonList(Sql.KEY_STAR, paras, customCompare);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 不能自定义参数compare 默认=
	 *
	 * @param paras
	 * @return
	 */
	public List<M> getCommonList(Okv paras) {
		return getCommonList(Sql.KEY_STAR, paras, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 不能自定义参数compare 默认=
	 *
	 * @param columns 指定查询的列
	 * @return
	 */
	public List<M> getCommonList(String columns) {
		return getCommonList(columns, null, false);
	}

	/**
	 * 得到不包含指定列的数据
	 *
	 * @param paras          查询条件
	 * @param customCompare  是否自定义比较符
	 * @param withoutColumns 不需要结果携带的列 多个 逗号隔开
	 * @return
	 */
	public List<M> getCommonListWithoutColumns(Okv paras, boolean customCompare, String... withoutColumns) {
		return daoTemplate(SQL_TPL_COMMON_LIST,
				Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras)
						.setIfNotBlank(SQL_TPL_KEY_COLUMNS, getTableSelectColumnStrWithout(withoutColumns))).find();
	}

	/**
	 * 得到表中的字段字符拼接，除了指定的withoutColumns
	 *
	 * @param withoutColumns 不需要结果携带的字段列 多个 逗号隔开
	 * @return
	 */
	public String getTableSelectColumnStrWithout(String... withoutColumns) {
		return JBoltArrayUtil.join(getTableSelectColumnsWithout(withoutColumns));
	}

	/**
	 * 得到表中的字段数组，除了指定的withoutColumns
	 *
	 * @param withoutColumns 不需要结果携带的字段列 多个 逗号隔开
	 * @return
	 */
	public String[] getTableSelectColumnsWithout(String... withoutColumns) {
		if (withoutColumns == null || withoutColumns.length == 0) {
			return new String[]{Sql.KEY_STAR};
		}
		if(withoutColumns.length == 1 && withoutColumns[0].contains(KEY_COMMA)){
			withoutColumns = JBoltArrayUtil.from3(withoutColumns[0]);
		}
		HashSet<String> tableColumns = new HashSet<>(dao()._getTable().getColumnNameSet());
		for (String col : withoutColumns) {
			tableColumns.remove(col.trim());
			tableColumns.remove(col.trim().toLowerCase());
			tableColumns.remove(col.trim().toUpperCase());
		}
		return tableColumns.size() == 0 ? new String[]{Sql.KEY_STAR} : tableColumns.toArray(new String[0]);
	}

	/**
	 * 得到表中的字段字符拼接，除了指定的withoutColumns
	 *
	 * @param withoutColumns 不需要结果携带的字段列 多个 逗号隔开
	 * @return
	 */
	public String getTableSelectColumnStrWithoutWithPre(String pre,String... withoutColumns) {
		return JBoltArrayUtil.join(getTableSelectColumnsWithoutWithPre(pre,withoutColumns));
	}
	/**
	 * 得到表中的字段字符数组，除了指定的withoutColumns
	 *
	 * @param pre            前缀
	 * @param withoutColumns 不需要结果携带的字段列 多个 逗号隔开
	 * @return
	 */
	public String[] getTableSelectColumnsWithoutWithPre(String pre, String... withoutColumns) {
		if(!pre.endsWith(".")){
			pre = pre + ".";
		}
		String[] tableColumns = getTableSelectColumnsWithout(withoutColumns);
		if(notOk(tableColumns)){
			return new String[]{pre+Sql.KEY_STAR};
		}
		int size = tableColumns.length;
		String temp;
		for(int i=0;i<size;i++){
			temp = tableColumns[i];
			tableColumns[i] = pre+temp;
		}
		return tableColumns;
//		return CollectionUtil.join(, KEY_COMMA, pre, null);
	}

	/**
	 * 得到不包含指定列的数据
	 *
	 * @param withoutColumns 不需要结果携带的字段列 多个 逗号隔开
	 * @return
	 */
	public List<M> getCommonListWithoutColumns(String withoutColumns) {
		return getCommonListWithoutColumns(null, false, withoutColumns);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 不能自定义参数compare 默认=
	 *
	 * @param columns 指定查询的列
	 * @param paras   查询条件
	 * @return
	 */
	public List<M> getCommonList(String columns, Okv paras) {
		return getCommonList(columns, paras, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 默认正序 不能自定义参数compare 默认=
	 *
	 * @param paras       查询条件
	 * @param orderColums 排序列
	 * @return
	 */
	public List<M> getCommonList(Okv paras, String orderColums) {
		if (notOk(orderColums)) {
			return getCommonList(paras);
		}
		int count = StrUtil.count(orderColums, KEY_COMMA);
		String orderTypes = "";
		for (int i = 0; i <= count; i++) {
			if (i == 0) {
				orderTypes = Sql.KEY_ASC_TRIMED;
			} else {
				orderTypes = orderTypes + KEY_COMMA + Sql.KEY_ASC_TRIMED;
			}
		}
		return getCommonList(Sql.KEY_STAR, paras, orderColums, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 不能自定义参数compare 默认=
	 *
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<M> getCommonList(String orderColumns, String orderTypes) {
		return getCommonList(Sql.KEY_STAR, null, orderColumns, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 不能自定义参数compare 默认=
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<M> getCommonList(Okv paras, String orderColumns, String orderTypes) {
		return getCommonList(Sql.KEY_STAR, paras, orderColumns, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 不能自定义参数compare 默认=
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<M> getCommonList(String columns, Okv paras, String orderColumns, String orderTypes) {
		return getCommonList(columns, paras, orderColumns, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 自定义参数compare
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param customCompare
	 * @return
	 */
	public List<M> getCommonList(Okv paras, String orderColumns, String orderTypes, boolean customCompare) {
		return getCommonList(Sql.KEY_STAR, paras, orderColumns, orderTypes, customCompare);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 不能自定义参数compare 默认=
	 *
	 * @param columns
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<M> getCommonList(String columns, String orderColumns, String orderTypes) {
		return getCommonList(columns, null, orderColumns, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 自定义参数compare
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param customCompare
	 * @return
	 */
	public List<M> getCommonList(String columns, Okv paras, String orderColumns, String orderTypes,
			boolean customCompare) {
		Okv conf = Okv.by(SQL_TPL_KEY_MYPARAS, paras).set(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare);
		if (isOk(columns)) {
			conf.set(SQL_TPL_KEY_COLUMNS, columns);
		}
		if (isOk(orderColumns)) {
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, JBoltArrayUtil.from(orderColumns, KEY_COMMA));
		}
		if (isOk(orderTypes)) {
			conf.set(SQL_TPL_KEY_ORDER_TYPES, JBoltArrayUtil.from(orderTypes, KEY_COMMA));
		}
		return daoTemplate(SQL_TPL_COMMON_LIST, conf).find();
	}

	/**
	 * 分页查询
	 *
	 * @param sql
	 * @return
	 */
	public Page<M> paginate(Sql sql) {
		Sql totalCountSql = ObjectUtil.clone(sql);
		if(sql.isQueryDistinct()){
			totalCountSql.distinctToDistinctCount();
		}else{
			totalCountSql.count();
		}
		return processPaginate(sql, totalCountSql);
	}

	/**
	 * 分页查询 返回Page<Record>
	 *
	 * @param sql
	 * @return
	 */
	public Page<Record> paginateRecord(Sql sql) {
		if (!sql.hasPage()) {
			throw new RuntimeException("Please set the values of PageNumber and PageSize in the SQL");
		}
		return paginateRecord(sql, false);
	}

	/**
	 * 获得所有字段合并字符串
	 * @param modelClass
	 * @return
	 */
	public String getAllTableColumnsStr(Class<? extends Model> modelClass){
		Set<String> tableColumns = TableMapping.me().getTable(modelClass).getColumnNameSet();
		List<String> selectColumns = new ArrayList<String>(tableColumns);
		return selectColumns.size() == 0 ? Sql.KEY_STAR : JBoltArrayUtil.join(selectColumns);
	}

	/**
	 * 获得所有字段合并字符串  加前缀
	 * @param modelClass
	 * @param pre           前缀字符
	 * @return
	 */
	public String getAllTableColumnsStr(Class<? extends Model> modelClass,String pre){
		Set<String> tableColumns = TableMapping.me().getTable(modelClass).getColumnNameSet();
		List<String> selectColumns = new ArrayList<String>(tableColumns);
		return selectColumns.size() == 0 ? (pre+Sql.KEY_STAR) : CollectionUtil.join(selectColumns,KEY_COMMA,pre,null);
	}

	/**
	 * 分页查询 返回Page<Record> 支持设置字段属性转为驼峰
	 *
	 * @param sql
	 * @param columnToCamelCase
	 * @return
	 */
	public Page<Record> paginateRecord(Sql sql, boolean columnToCamelCase) {
		Sql totalCountSql = ObjectUtil.clone(sql);
		if(sql.isQueryDistinct()){
			totalCountSql.distinctToDistinctCount();
		}else{
			totalCountSql.count();
		}
		return paginateRecord(sql, totalCountSql, columnToCamelCase);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 自定义参数compare 分页查询
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @param or
	 * @return
	 */
	public Page<M> paginate(String columns, Okv paras, String orderColumns, String orderTypes, int pageNumber,
			int pageSize, boolean customCompare, boolean or) {
		Okv conf = Okv.by(SQL_TPL_KEY_MYPARAS, paras).set(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare);
		conf.set(SQL_TPL_KEY_OR, or);
		conf.setIfNotBlank(SQL_TPL_KEY_COLUMNS, columns);
		if (isOk(orderColumns)) {
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, JBoltArrayUtil.from(orderColumns, KEY_COMMA));
		}
		if (isOk(orderTypes)) {
			conf.set(SQL_TPL_KEY_ORDER_TYPES, JBoltArrayUtil.from(orderTypes, KEY_COMMA));
		}
		return daoTemplate(SQL_TPL_COMMON_LIST, conf).paginate(pageNumber, pageSize);
	}

	/**
	 * 根据关键词分页查询
	 *
	 * @param orderColumn
	 * @param orderType
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @param matchColumns
	 * @return
	 */
	public Page<M> paginateByKeywords(String orderColumn, String orderType, int pageNumber, int pageSize,
			String keywords, String matchColumns) {
		return paginateByKeywords(orderColumn, orderType, pageNumber, pageSize, keywords, matchColumns, null);
	}

	/**
	 * 根据关键词分页查询 默认倒序
	 *
	 * @param orderColumn
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @param matchColumns
	 * @return
	 */
	public Page<M> paginateByKeywords(String orderColumn, int pageNumber, int pageSize, String keywords,
			String matchColumns) {
		return paginateByKeywords(orderColumn, Sql.KEY_DESC_TRIMED, pageNumber, pageSize, keywords, matchColumns, null);
	}

	/**
	 * 根据关键词分页查询 默认倒序
	 *
	 * @param orderColumn
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @param matchColumns
	 * @param otherParas
	 * @return
	 */
	public Page<M> paginateByKeywords(String orderColumn, int pageNumber, int pageSize, String keywords,
			String matchColumns, Okv otherParas) {
		return paginateByKeywords(orderColumn, Sql.KEY_DESC_TRIMED, pageNumber, pageSize, keywords, matchColumns,
				otherParas);
	}

	/**
	 * 根据关键词分页查询
	 *
	 * @param orderColumn
	 * @param orderType
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @param matchColumns
	 * @param otherParas
	 * @return
	 */
	public Page<M> paginateByKeywords(String orderColumn, String orderType, int pageNumber, int pageSize,
			String keywords, String matchColumns, Okv otherParas) {
		return paginateByKeywords(Sql.KEY_STAR, orderColumn, orderType, pageNumber, pageSize, keywords, matchColumns,
				otherParas);
	}

	/**
	 * 根据关键词匹配分页查询
	 *
	 * @param returnColumns
	 * @param orderColumn
	 * @param orderType
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @param matchColumns
	 * @param otherParas
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Page<M> paginateByKeywords(String returnColumns, String orderColumn, String orderType, int pageNumber,
			int pageSize, String keywords, String matchColumns, Okv otherParas) {
		if (notOk(matchColumns)) {
			return emptyPage(pageSize);
		}
		Sql sql = selectSql().select(returnColumns).page(pageNumber, pageSize);
		Sql totalCountSql = selectSql().count();
		if (isOk(orderColumn) && isOk(orderType)) {
			sql.orderBy(orderColumn, Sql.KEY_DESC_TRIMED.equals(orderType));
		} else {
			sql.orderByIdAscIfPgSql();
		}
		if (otherParas != null && otherParas.size() > 0) {
			Set<String> keys = otherParas.keySet();
			sql.bracketLeft();
			totalCountSql.bracketLeft();
			for (String key : keys) {
				sql.eq(key, otherParas.get(key));
				totalCountSql.eq(key, otherParas.get(key));
			}
			sql.bracketRight();
			totalCountSql.bracketRight();
		}
		// 如果没有给keywords字段
		if (notOk(keywords)) {
			return processPaginate(sql, totalCountSql);
		}
		// 如果给了Keywords
		String[] columns = JBoltArrayUtil.from(matchColumns, KEY_COMMA);
		if (columns.length == 0) {
			return emptyPage(pageSize);
		}
		int size = columns.length;
		sql.bracketLeft();
		totalCountSql.bracketLeft();
		keywords = keywords.trim();
		for (int i = 0; i < size; i++) {
			sql.like(columns[i], keywords);
			totalCountSql.like(columns[i], keywords);
			if (i < size - 1) {
				sql.or();
				totalCountSql.or();
			}
		}
		sql.bracketRight();
		totalCountSql.bracketRight();
		return processPaginate(sql, totalCountSql);
	}

	private Page<M> processPaginate(Sql sql, Sql totalCountSql) {
		if (!sql.hasPage()) {
			throw new RuntimeException("Please set the values of PageNumber and PageSize in the SQL");
		}
		totalCountSql.fromIfBlank(table());
		totalCountSql.clearOrderBy();
		totalCountSql.clearPage();
		Integer totalRow = queryInt(totalCountSql);
		if (totalRow == null || totalRow == 0) {
			return emptyPage(sql.getPageSize());
		}
		sql.fromIfBlank(table());
		List<M> list = find(sql);
		int totalPage = (totalRow / sql.getPageSize()) + (totalRow % sql.getPageSize() > 0 ? 1 : 0);
		return new Page<M>(list, sql.getPageNumber(), sql.getPageSize(), totalPage, totalRow);
	}

	/**
	 * 分页查询
	 *
	 * @param sql
	 * @param totalCountSql
	 * @param columnToCamelCase
	 * @return
	 */
	public Page<Record> paginateRecord(Sql sql, Sql totalCountSql, boolean columnToCamelCase) {
		if (!sql.hasPage()) {
			throw new RuntimeException("Please set the values of PageNumber and PageSize in the SQL");
		}
		totalCountSql.fromIfBlank(table());
		totalCountSql.clearOrderBy();
		totalCountSql.clearPage();
		Integer totalRow = queryInt(totalCountSql);
		if (totalRow == null || totalRow == 0) {
			return emptyPage(sql.getPageSize());
		}
		sql.fromIfBlank(table());
		List<Record> list = findRecord(sql);
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(list);
		}
		int pageSize = sql.getPageSize();
		int pageNumber = sql.getPageNumber();
		int totalPage = (totalRow / pageSize) + (totalRow % pageSize > 0 ? 1 : 0);
		return new Page<Record>(list, pageNumber, pageSize, totalPage, totalRow);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(String columns, Okv paras, String orderColumns, String orderTypes, int pageNumber,
			int pageSize) {
		return paginate(columns, paras, orderColumns, orderTypes, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @param or
	 * @return
	 */
	public Page<M> paginate(Okv paras, String orderColumns, String orderTypes, int pageNumber, int pageSize,
			boolean customCompare, boolean or) {
		return paginate(Sql.KEY_STAR, paras, orderColumns, orderTypes, pageNumber, pageSize, customCompare, or);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<M> paginate(Okv paras, String orderColumns, String orderTypes, int pageNumber, int pageSize,
			boolean customCompare) {
		return paginate(Sql.KEY_STAR, paras, orderColumns, orderTypes, pageNumber, pageSize, customCompare, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(Okv paras, String orderColumns, String orderTypes, int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, paras, orderColumns, orderTypes, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param orderColumns
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(String orderColumns, int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, null, orderColumns, Sql.KEY_DESC_TRIMED, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(String orderColumns, String orderTypes, int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, null, orderColumns, orderTypes, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 返回指定列 可以排序 条件都是等于 分页查询
	 *
	 * @param columns
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(String columns, String orderColumns, String orderTypes, int pageNumber, int pageSize) {
		return paginate(columns, null, orderColumns, orderTypes, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 不尅一可以排序 条件自定义 customCompare 分页查询
	 *
	 * @param columns
	 * @param paras
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<M> paginate(String columns, Okv paras, int pageNumber, int pageSize, boolean customCompare) {
		Okv conf = Okv.by(SQL_TPL_KEY_MYPARAS, paras).set(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare);
		if (isOk(columns)) {
			conf.set(SQL_TPL_KEY_COLUMNS, columns);
		}
		return daoTemplate(SQL_TPL_COMMON_LIST, conf).paginate(pageNumber, pageSize);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 条件自定义 customCompare 分页查询
	 *
	 * @param paras
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<M> paginate(Okv paras, int pageNumber, int pageSize, boolean customCompare) {
		return paginate(Sql.KEY_STAR, paras, pageNumber, pageSize, customCompare);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param paras
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(Okv paras, int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, paras, pageNumber, pageSize, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param paras
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<M> paginate(Okv paras, int pageSize, boolean customCompare) {
		return paginate(Sql.KEY_STAR, paras, 1, pageSize, customCompare);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, null, pageNumber, pageSize, false);
	}

	/**
	 * 按照sql模板分页查询
	 *
	 * @param key
	 * @param data
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginateBySqlTemplate(String key, Okv data, int pageNumber, int pageSize) {
		return paginateBySqlTemplate(key, data, pageNumber, pageSize, null, null);
	}

	/**
	 * 按照sql模板分页查询
	 *
	 * @param key
	 * @param data
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginateBySqlTemplate(String key, Okv data, int pageNumber, int pageSize, String orderColumns,
			String orderTypes) {
		if (isOk(orderColumns)) {
			data.set(SQL_TPL_KEY_ORDER_COLUMNS, JBoltArrayUtil.from(orderColumns, KEY_COMMA));
		}
		if (isOk(orderTypes)) {
			data.set(SQL_TPL_KEY_ORDER_TYPES, JBoltArrayUtil.from(orderTypes, KEY_COMMA));
		}
		return daoTemplate(key, data).paginate(pageNumber, pageSize);
	}

	/**
	 * 按照sql模板分页查询 返回Record
	 *
	 * @param key
	 * @param data
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginateByDbSqlTemplate(String key, Okv data, int pageNumber, int pageSize) {
		return paginateByDbSqlTemplate(key, data, pageNumber, pageSize, false);
	}

	/**
	 * 按照sql模板分页查询 返回Record
	 *
	 * @param key
	 * @param data
	 * @param pageNumber
	 * @param pageSize
	 * @param columnToCamelCase 列名是否转驼峰
	 * @return
	 */
	public Page<Record> paginateByDbSqlTemplate(String key, Okv data, int pageNumber, int pageSize,
			boolean columnToCamelCase) {
		Page<Record> pageData = dbTemplate(key, data).paginate(pageNumber, pageSize);
		if (columnToCamelCase && pageData != null && isOk(pageData.getList())) {
			JBoltCamelCaseUtil.keyToCamelCase(pageData);
		}
		return pageData;
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param paras
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(Okv paras, int pageSize) {
		return paginate(Sql.KEY_STAR, paras, 1, pageSize, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param pageSize
	 * @return
	 */
	public Page<M> paginate(int pageSize) {
		return paginate(Sql.KEY_STAR, null, 1, pageSize, false);
	}

	/**
	 * 通用根据ID删除数据 真删除
	 *
	 * @param id
	 * @return
	 */
	public Ret realDeleteById(Object id) {
		return realDeleteById(id, null);
	}

	/**
	 * 通用根据ID删除数据 真删除 不判断假删除标识
	 *
	 * @param id
	 * @return
	 */
	public Ret deleteById(Object id) {
		return deleteById(id, null);
	}

	/**
	 * 通用根据ID删除数据
	 *
	 * @param id
	 * @param kv
	 * @return
	 */
	public Ret deleteById(Object id, Kv kv) {
		return deleteById(id, false, kv);
	}

	/**
	 * 通用根据ID删除数据 真删除
	 *
	 * @param id
	 * @param kv
	 * @return
	 */
	public Ret realDeleteById(Object id, Kv kv) {
		return realDeleteById(id, false, kv);
	}

	/**
	 * 根据ID 批量删除 指定分隔符版
	 *
	 * @param ids
	 * @param split 分隔符
	 * @param checkCanDelete 检测是否能删除
	 * @return
	 */
	public Ret deleteByIds(String ids, String split, boolean checkCanDelete) {
		return deleteByIds(JBoltArrayUtil.from3(ids, split), checkCanDelete);
	}

	/**
	 * 根据ID 批量删除 指定分隔符版
	 *
	 * @param ids
	 * @param split 分隔符
	 * @param checkCanDelete 检测是否能删除
	 * @return
	 */
	public Ret realDeleteByIds(String ids, String split, boolean checkCanDelete) {
		return realDeleteByIds(JBoltArrayUtil.from3(ids, split), checkCanDelete);
	}

	/**
	 * 根据ID 批量删除 指定分隔符版
	 *
	 * @param ids
	 * @param split 分隔符
	 * @return
	 */
	public Ret deleteByIds(String ids, String split) {
		return deleteByIds(ids, split, false);
	}

	/**
	 * 根据ID 批量删除 指定分隔符版 真删除
	 *
	 * @param ids
	 * @param split 分隔符
	 * @return
	 */
	public Ret realDeleteByIds(String ids, String split) {
		return realDeleteByIds(ids, split, false);
	}

	/**
	 * 根据ID 批量删除 默认分隔符是逗号
	 *
	 * @param ids
	 * @return
	 */
	public Ret deleteByIds(String ids) {
		return deleteByIds(ids, false);
	}

	/**
	 * 根据ID 批量删除 默认分隔符是逗号 真删除
	 *
	 * @param ids
	 * @return
	 */
	public Ret realDeleteByIds(String ids) {
		return realDeleteByIds(ids, false);
	}

	/**
	 * 根据ID 批量删除 默认分隔符是逗号
	 *
	 * @param ids
	 * @param checkCanDelete 检测是否能删除
	 * @return
	 */
	public Ret deleteByIds(String ids, boolean checkCanDelete) {
		return deleteByIds(ids, KEY_COMMA, checkCanDelete);
	}

	/**
	 * 根据ID 批量删除 默认分隔符是逗号
	 *
	 * @param ids
	 * @param checkCanDelete 检测是否能删除
	 * @return
	 */
	public Ret realDeleteByIds(String ids, boolean checkCanDelete) {
		return realDeleteByIds(ids, KEY_COMMA, checkCanDelete);
	}

	/**
	 * 根据ID 批量删除
	 *
	 * @param ids
	 * @return
	 */
	public Ret deleteByIds(Object[] ids) {
		return deleteByIds(ids, false, false);
	}

	/**
	 * 根据ID 批量删除 真删除
	 *
	 * @param ids
	 * @return
	 */
	public Ret realDeleteByIds(Object[] ids) {
		return deleteByIds(ids, false, true);
	}

	/**
	 * 根据ID 批量删除
	 *
	 * @param ids
	 * @param checkCanDelete 检测是否能删除
	 * @return
	 */
	public Ret deleteByIds(Object[] ids, boolean checkCanDelete) {
		return deleteByIds(ids, checkCanDelete, false);
	}

	/**
	 * 根据ID 批量删除
	 *
	 * @param ids
	 * @param checkCanDelete
	 * @return
	 */
	public Ret realDeleteByIds(Object[] ids, boolean checkCanDelete) {
		return deleteByIds(ids, checkCanDelete, true);
	}

	/**
	 * 根据ID 批量删除
	 *
	 * @param ids
	 * @param checkCanDelete
	 * @param realDelete
	 * @return
	 */
	public Ret deleteByIds(Object[] ids, boolean checkCanDelete, boolean realDelete) {
		if (notOk(ids)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		Ret ret;
		for (Object id : ids) {
			if (realDelete) {
				ret = realDeleteById(id, checkCanDelete);
			} else {
				ret = deleteById(id, checkCanDelete);
			}
			if (ret.isFail()) {
				return ret;
			}
		}
		String errorMsg = afterDeleteByIds(ids);
		if(isOk(errorMsg)){
			return fail(errorMsg);
		}
		return SUCCESS;
	}

	/**
	 * 根据ID 批量恢复
	 *
	 * @param ids
	 * @param split
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverByIds(String ids,String split, boolean checkCanRecover) {
		return recoverByIds(JBoltArrayUtil.from3(ids,split),checkCanRecover);
	}
	/**
	 * 根据ID 批量恢复
	 *
	 * @param ids
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverByIds(String ids, boolean checkCanRecover) {
		return recoverByIds(ids,KEY_COMMA,checkCanRecover);
	}

	/**
	 * 根据ID 批量恢复 默认不检测是否可以恢复
	 *
	 * @param ids
	 * @return
	 */
	public Ret recoverByIds(String ids) {
		return recoverByIds(ids,KEY_COMMA,false);
	}
	/**
	 * 根据ID 批量恢复
	 *
	 * @param ids
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverByIds(Object[] ids, boolean checkCanRecover) {
		if (notOk(ids)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		Ret ret;
		for (Object id : ids) {
			ret = recoverById(id, checkCanRecover);
			if (ret.isFail()) {
				return ret;
			}
		}
		return SUCCESS;
	}

	/**
	 * 通用根据ID删除数据
	 *
	 * @param id
	 * @param checkCanDelete
	 * @return
	 */
	public Ret deleteById(Object id, boolean checkCanDelete) {
		return deleteById(id, checkCanDelete, null);
	}

	/**
	 * 通用根据ID删除数据 真删除
	 *
	 * @param id
	 * @param checkCanDelete
	 * @return
	 */
	public Ret realDeleteById(Object id, boolean checkCanDelete) {
		return realDeleteById(id, checkCanDelete, null);
	}

	/**
	 * 通用根据ID删除数据
	 *
	 * @param id
	 * @param checkCanDelete
	 * @param kv             额外数据
	 * @return
	 */
	public Ret deleteById(Object id, boolean checkCanDelete, Kv kv) {
		return deleteById(id, checkCanDelete, true, false, kv);
	}

	/**
	 * 通用根据ID删除数据 真删除
	 *
	 * @param id
	 * @param checkCanDelete
	 * @param kv             额外数据
	 * @return
	 */
	public Ret realDeleteById(Object id, boolean checkCanDelete, Kv kv) {
		return deleteById(id, checkCanDelete, true, true, kv);
	}

	/**
	 * 通用根据ID删除数据 需要先检查是否被其他地方使用
	 *
	 * @param id
	 * @param checkCanDelete
	 * @param returnDeleteData
	 * @param realDelete
	 * @param kv               额外数据
	 * @return
	 */
	public Ret deleteById(Object id, boolean checkCanDelete, boolean returnDeleteData, boolean realDelete, Kv kv) {
		if (JBoltConfig.DEMO_MODE) {
			return fail(JBoltMsg.DEMO_MODE_CAN_NOT_DELETE);
		}
		if (notOk(id)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		M m = findById(id);
		if (m == null) {
			return fail(JBoltMsg.DATA_NOT_EXIST);
		}

		if (checkCanDelete) {
			String msg = checkCanDelete(m, kv);
			if (isOk(msg)) {
				return fail(msg);
			}
		}
		boolean success = false;
		if (realDelete) {
			success = m.delete();
		} else {
			if (m.hasColumn("is_deleted")) {
				// 如果存在 假删控制字段 就执行假删除
				m.set("is_deleted", TRUE);
				m.autoProcessDeleteUserId();
				m.autoProcessDeleteTime();
				success = m.update();
			} else if (m.hasColumn("IS_DELETED")) {
				// 如果存在 假删控制字段 就执行假删除
				m.set("IS_DELETED", TRUE);
				m.autoProcessDeleteUserId();
				m.autoProcessDeleteTime();
				success = m.update();
			} else {
				success = m.delete();
			}
		}
		if (success) {
			String msg = null;
			if (realDelete) {
				msg = afterRealDelete(m, kv);
			} else {
				msg = afterDelete(m, kv);
			}

			if (isOk(msg)) {
				return fail(msg);
			}
		}
		// 需要返回删除的数据
		if (returnDeleteData) {
			return success ? success(m, JBoltMsg.SUCCESS) : fail();
		}
		return ret(success);
	}

	/**
	 * 检测数据是否可以被恢复
	 *
	 * @param model
	 * @return
	 */
	public String checkCanRecover(M model) {
		return null;
	}
	/**
	 * 根据ID追回假删数据 不检测是否可以恢复
	 *
	 * @param id
	 * @return
	 */
	public Ret recoverById(Object id) {
		return recoverById(id,false);
	}

	/**
	 * 根据ID追回假删数据
	 *
	 * @param id
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverById(Object id, boolean checkCanRecover) {
		if (JBoltConfig.DEMO_MODE) {
			return fail(JBoltMsg.DEMO_MODE_CAN_NOT_DELETE);
		}
		if (notOk(id)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		M m = findById(id);
		if (m == null) {
			return fail(JBoltMsg.DATA_NOT_EXIST);
		}

		boolean success = false;
		if (m.hasColumn("is_deleted")) {
			if (checkCanRecover) {
				String msg = checkCanRecover(m);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
			// 如果存在 假删控制字段 就执行假删除
			m.set("is_deleted", FALSE);
			success = m.update();
			if (success) {
				String msg = afterRecover(m);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
		} else if (m.hasColumn("IS_DELETED")) {
			if (checkCanRecover) {
				String msg = checkCanRecover(m);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
			// 如果存在 假删控制字段 就执行假删除
			m.set("IS_DELETED", FALSE);
			success = m.update();
			if (success) {
				String msg = afterRecover(m);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
		} else {
			return fail("此表数据无法执行追回操作，表内不包含is_deleted字段");
		}

		return ret(success);
	}

	/**
	 * 检测数据是否可以被其它数据引用 带额外数据
	 *
	 * @param model
	 * @param kv    额外参数
	 * @return
	 */
	public String checkInUse(M model, Kv kv) {
		return null;
	}

	/**
	 * 检测数据是否可以被删除 带额外数据
	 *
	 * @param model
	 * @param kv    额外参数
	 * @return
	 */
	public String checkCanDelete(M model, Kv kv) {
		return null;
	}

	/**
	 * 检测数据是否字段是否可以执行切换true false
	 *
	 * @param model
	 * @param column
	 * @param kv
	 * @return
	 */
	public String checkCanToggle(M model, String column, Kv kv) {
		return null;
	}

	/**
	 * checkCanToggle之后 处理完一个后 更新之前调用
	 * @param model
	 * @param column
	 * @param kv
	 * @return
	 */
	protected String beforeToggleBoolean(M model,String column, Kv kv) {
		return null;
	}

	/**
	 * checkCanToggle之后 所有columns处理完 更新之前执行
	 * @param model
	 * @param kv
	 * @return
	 */
	protected String beforeToggleBoolean(M model, Kv kv) {
		return null;
	}
	/**
	 * 额外需要处理toggle操作
	 *
	 * @param model
	 * @param column
	 * @param kv
	 * @return
	 */
	protected String afterToggleBoolean(M model, String column, Kv kv) {
		return null;
	}

	/**
	 * 删除成功后需要处理的事情
	 *
	 * @param model
	 * @param kv
	 * @return
	 */
	protected String afterDelete(M model, Kv kv) {
		return null;
	}

	/**
	 * 批量删除成功后需要处理的事情
	 *
	 * @param ids
	 * @return
	 */
	protected String afterDeleteByIds(Object[] ids) {
		return null;
	}

	/**
	 * 恢复成功后需要处理的事情
	 *
	 * @param model
	 * @return
	 */
	protected String afterRecover(M model) {
		return null;
	}

	/**
	 * 真删除成功后需要处理的事情
	 *
	 * @param model
	 * @param kv
	 * @return
	 */
	protected String afterRealDelete(M model, Kv kv) {
		return null;
	}

	/**
	 * 判断name是否存在相同数据 排除指定ID
	 *
	 * @param nameValue
	 * @param excludedIdValue
	 * @return
	 */
	public boolean existsName(String nameValue, Object excludedIdValue) {
		return existsName(nameValue, excludedIdValue, null);
	}

	/**
	 * 判断sn是否存在相同数据 排除指定ID
	 *
	 * @param snValue
	 * @param excludedIdValue
	 * @return
	 */
	public boolean existsSn(String snValue, Object excludedIdValue) {
		return existsSn(snValue, excludedIdValue, null);
	}

	/**
	 * 判断name是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param nameValue
	 * @param excludedIdValue
	 * @param pidValue
	 * @return
	 */
	public boolean existsName(String nameValue, Object excludedIdValue, Object pidValue) {
		return existsName(nameValue, excludedIdValue, PID, pidValue);
	}

	/**
	 * 判断sn是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param snValue
	 * @param excludedIdValue
	 * @param pidValue
	 * @return
	 */
	public boolean existsSn(String snValue, Object excludedIdValue, Object pidValue) {
		return existsSn(snValue, excludedIdValue, PID, pidValue);
	}

	/**
	 * 判断name是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param nameValue
	 * @param excludedIdValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsName(String nameValue, Object excludedIdValue, String pidName, Object pidValue) {
		return exists(NAME, nameValue, excludedIdValue, pidName, pidValue);
	}

	/**
	 * 判断sn是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param snValue
	 * @param excludedIdValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsSn(String snValue, Object excludedIdValue, String pidName, Object pidValue) {
		return exists(SN, snValue, excludedIdValue, pidName, pidValue);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnValue
	 * @param excludedIdValue
	 * @param pidValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnValue, Object excludedIdValue, Object pidValue) {
		return exists(columnName, columnValue, excludedIdValue, PID, pidValue);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnValue
	 * @param excludedIdValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnValue, Object excludedIdValue, String pidName,
			Object pidValue) {
		Sql sql = selectSql().select(primaryKey()).eqQM(columnName).idNotEqQM(primaryKey()).first();
		Object existId = null;
		if (StrKit.notBlank(pidName) && pidValue != null) {
			sql.eqQM(pidName);
			existId = queryColumn(sql, columnValue, excludedIdValue, pidValue);
		} else {
			existId = queryColumn(sql, columnValue, excludedIdValue);
		}
		return isOk(existId);
	}

	/**
	 * 判断是否存在满足条件的数据
	 *
	 * @param sql
	 * @return
	 */
	public boolean exists(Sql sql) {
		sql.select(primaryKey()).first();
		Object existId = queryColumn(sql);
		return isOk(existId);
	}

	/**
	 * 判断是否存在满足条件的数据
	 *
	 * @param sql
	 * @param whereValues
	 * @return
	 */
	public boolean exists(Sql sql, Object... whereValues) {
		sql.select(primaryKey()).first();
		Object existId = queryColumn(sql, whereValues);
		return isOk(existId);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnvalue
	 * @param excludedIdValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnvalue, Object excludedIdValue) {
		return exists(columnName, columnvalue, excludedIdValue, null, null);
	}

	/**
	 * 判断在子节点里是否存在指定列值数据
	 *
	 * @param columnName
	 * @param columnvalue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsInSons(String columnName, Object columnvalue, String pidName, Object pidValue) {
		return exists(columnName, columnvalue, -1, pidName, pidValue);
	}

	/**
	 * 判断数据库是否存在指定id的值
	 *
	 * @param idValue
	 * @return
	 */
	public boolean existsById(Object idValue) {
		if (notOk(idValue)) {
			//如果传值有问题
			throw new RuntimeException(JBoltMsg.PARAM_ERROR);
		}
		Sql sql = selectSql().selectId().idEqQM(primaryKey()).first();
		Object existId = queryColumn(sql, idValue);
		return isOk(existId);
	}

	/**
	 * 判断是否存在子节点
	 *
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsSon(String pidName, Object pidValue) {
		if (notOk(pidName) || notOk(pidValue)) {
			// 如果传值有问题
			throw new RuntimeException(JBoltMsg.PARAM_ERROR);
		}
		Sql sql = selectSql().selectId().eqQM(pidName).first();
		Object existId = queryColumn(sql, pidValue);
		return isOk(existId);
	}

	/**
	 * 判断是否存在子节点
	 *
	 * @param pidValue
	 * @return
	 */
	public boolean existsSon(Object pidValue) {
		return existsSon(PID, pidValue);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnValue) {
		return exists(columnName, columnValue, -1);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @return
	 */
	public boolean existsName(String nameValue) {
		return existsName(nameValue, -1);
	}

	/**
	 * 判断sn值为指定值的数据是否存在
	 *
	 * @param snValue
	 * @return
	 */
	public boolean existsSn(String snValue) {
		return existsSn(snValue, -1);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @param excludedIdValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsNameWithPid(String nameValue,Object excludedIdValue, String pidName, Object pidValue) {
		return existsName(nameValue, excludedIdValue, pidName, pidValue);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsNameWithPid(String nameValue, String pidName, Object pidValue) {
		return existsNameWithPid(nameValue, -1, pidName, pidValue);
	}

	/**
	 * 判断sn值为指定值的数据是否存在
	 * @param snValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsSnWithPid(String snValue, String pidName, Object pidValue) {
		return existsSn(snValue, -1, pidName, pidValue);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @param pidValue
	 * @return
	 */
	public boolean existsNameWithPid(String nameValue, Object pidValue) {
		return existsNameWithPid(nameValue, PID, pidValue);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @param pidValue
	 * @param excludedIdValue
	 * @return
	 */
	public boolean existsNameWithPid(String nameValue, Object pidValue,Object excludedIdValue) {
		return existsNameWithPid(nameValue,excludedIdValue, PID, pidValue);
	}

	/**
	 * 判断sn值为指定值的数据是否存在
	 *
	 * @param snValue
	 * @param pidValue
	 * @return
	 */
	public boolean existsSnWithPid(String snValue, Object pidValue) {
		return existsSnWithPid(snValue, PID, pidValue);
	}

	/**
	 * 根据ID获得一条数据 自动处理缓存
	 *
	 * @param id
	 * @return
	 */
	public M findById(Object id) {
		if (notOk(id)) {
			return null;
		}
		return dao().findById(id);
	}

	/**
	 * 根据ID获得一条数据 直接从数据库查询
	 *
	 * @param id
	 * @return
	 */
	public M superFindById(Object id) {
		if (notOk(id)) {
			return null;
		}
		return dao().superFindById(id);
	}

	/**
	 * 根据ID获得一条数据 去掉指定的列
	 *
	 * @param id
	 * @param withoutColumns 不需要的列
	 * @return
	 */
	public M findByIdWithoutColumns(Object id, String... withoutColumns) {
		if (notOk(id)) {
			return null;
		}
		if (notOk(withoutColumns)) {
			return findById(id);
		}
		if(withoutColumns.length == 1 && withoutColumns[0].contains(",")){
			withoutColumns = JBoltArrayUtil.from3(withoutColumns[0]);
		}
		String[] columns = getTableSelectColumnsWithout(withoutColumns);
		return findByIdLoadColumns(id, columns);
	}

	/**
	 * 根据ID获得一条数据 指定需要的列
	 * 经过缓存
	 * @param id
	 * @param columns 需要的列
	 * @return
	 */
	public M findByIdLoadColumns(Object id, String... columns) {
		if (notOk(id)) {
			return null;
		}
		if (notOk(columns)) {
			return findById(id);
		}
		if(columns.length == 1 && columns[0].contains(",")){
			columns = JBoltArrayUtil.from3(columns[0]);
		}
		String withColumns = JBoltArrayUtil.join(columns, KEY_COMMA);
		return dao().findByIdLoadColumns(id, withColumns);
	}

	/**
	 * 根据ID获得一条数据 指定需要的列
	 * 不经过缓存
	 *
	 * @param id
	 * @param columns 需要的列
	 * @return
	 */
	public M superFindByIdLoadColumns(Object id, String... columns) {
		if (notOk(id)) {
			return null;
		}
		if (notOk(columns)) {
			return findById(id);
		}
		if(columns.length == 1 && columns[0].contains(",")){
			columns = JBoltArrayUtil.from3(columns[0]);
		}
		String withColumns = JBoltArrayUtil.join(columns, KEY_COMMA);
		return dao().superFindByIdLoadColumns(id, withColumns);
	}

	/**
	 * 根据ID获得一条数据 指定的列值
	 *
	 * @param id
	 * @param column 需要的列
	 * @return
	 */
	public <T> T getOneColumnValueById(Object id, String column) {
		if (notOk(column)) {
			return null;
		}
		M m = findByIdLoadColumns(id, column);
		return m == null ? null : m.get(column);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param paras
	 * @return
	 */
	public M findFirst(Okv paras) {
		return findFirst(paras, false);
	}

	/**
	 * 多主键表根据多主键值获取一个Model
	 * @return
	 */
	public M findByIds(Object... idValues){
		return dao().findByIds(idValues);
	}
	/**
	 * 根据多个IDS字符串 获得一组数据 自动处理缓存
	 * 用于少量读取获取
	 *
	 * @param idsStr
	 * @return
	 */
	public List<M> getListByIds(String idsStr) {
		if (notOk(idsStr)) {
			return null;
		}
		return getListByIds(JBoltArrayUtil.from3(idsStr,KEY_COMMA));
	}
	/**
	 * 根据多个IDS获得一组数据 自动处理缓存
	 * 用于少量读取获取
	 *
	 * @param ids
	 * @return
	 */
	public List<M> getListByIds(Object[] ids) {
		if (notOk(ids)) {
			return null;
		}
		if(dao()._isIdCacheEnable()){
			List<M> ms = new ArrayList<M>();
			M m;
			for(Object id:ids){
				m = findById(id);
				if(m!=null){
					ms.add(m);
				}
			}
			return ms;
		}
		String primaryKey = primaryKey();
		return find(selectSql().in(primaryKey,ids).asc(primaryKey));
	}
	/**
	 * 根据ID获取数据record
	 * @param id
	 * @return
	 */
	public Record findRecordById(Object id){
		return Db.use(dataSourceConfigName()).findById(table(),primaryKey(),id);
	}

	/**
	 * 根据ID获取数据record
	 * @param id
	 * @param columnToCamelCase
	 * @return
	 */
	public Record findRecordById(Object id,boolean columnToCamelCase,String... returnColumns){
		M m = null;
		if(dao()._isIdCacheEnable()){
			m = findById(id);
		}
		if(isOk(returnColumns)){
			return findFirstRecord(selectSql().select(returnColumns).idEq(id),columnToCamelCase);
		}
		Record record = Db.use(dataSourceConfigName()).findById(table(),primaryKey(),id);
		if(columnToCamelCase){
			JBoltCamelCaseUtil.keyToCamelCase(record);
		}
		return record;
	}


	/**
	 * 根据多个IDS获得一组数据 自动处理缓存
	 * 用于少量读取获取
	 *
	 * @param ids
	 * @return
	 */
	public List<Record> getRecordListByIds(Object[] ids) {
		return getRecordListByIds(ids,false);
	}


	/**
	 * 根据多个IDS获得一组数据 自动处理缓存
	 * 用于少量读取获取
	 *
	 * @param ids
	 * @param columnToCamelCase
	 * @return
	 */
	public List<Record> getRecordListByIds(Object[] ids,boolean columnToCamelCase,String... returnColumns) {
		if (notOk(ids)) {
			return null;
		}
		boolean needReturnColumns = isOk(returnColumns);
		if(dao()._isIdCacheEnable()){
			List<Record> ms = new ArrayList<Record>();
			M m;
			for(Object id:ids){
				m = findById(id);
				if(m!=null){
//					if(needReturnColumns && !JBoltCacheType.REDIS.equals(JBoltConfig.JBOLT_CACHE_TYPE)){
//						m = ObjectUtil.clone(m);
//						m.remove(getTableSelectColumnsWithout(returnColumns));
//					}
					ms.add(m.toRecord());
				}
			}
			if(columnToCamelCase){
				JBoltCamelCaseUtil.keyToCamelCase(ms);
			}
			return ms;
		}
		String primaryKey = primaryKey();
		return findRecord(selectSql().select(returnColumns).in(primaryKey,ids).asc(primaryKey),columnToCamelCase);
	}


	/**
	 * 得到符合条件的第一个
	 *
	 * @param sql
	 * @return
	 */
	public M findFirst(Sql sql) {
		if (sql.isPrepared()) {
			return dao().findFirst(sql.toSql(), sql.getWhereValues());
		}
		return dao().findFirst(sql.toSql());
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param sql
	 * @return
	 */
	public Record findFirstRecord(Sql sql) {
		return findFirstRecord(sql, false);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param sql
	 * @param columnToCamelCase 是否驼峰key
	 * @return
	 */
	public Record findFirstRecord(Sql sql, boolean columnToCamelCase) {
		Record record = null;
		if (sql.isPrepared()) {
			record = Db.use(dataSourceConfigName()).findFirst(sql.toSql(), sql.getWhereValues());
		} else {
			record = Db.use(dataSourceConfigName()).findFirst(sql.toSql());
		}
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(record);
		}
		return record;
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public M findFirst(Okv paras, boolean customCompare) {
		if (JBoltConfig.isMysql()) {
			return findFirst(paras, null, null, customCompare);
		}
		return findFirst(paras, ID, Sql.KEY_ASC_TRIMED, customCompare);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public M findFirst(Okv paras, String orderColumns, String orderTypes) {
		return findFirst(paras, orderColumns, orderTypes, false);
	}

	/**
	 * 得到符合条件的第一个 返回Model
	 *
	 * @return
	 */
	public M findFirst() {
		return findFirst(OKV_EMPTY);
	}

	/**
	 * 得到符合条件的第一个 返回record
	 *
	 * @return
	 */
	public Record findFirstRecord() {
		return findFirstRecord(selectSql());
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param customCompare
	 * @return
	 */
	public M findFirst(Okv paras, String orderColumns, String orderTypes, boolean customCompare) {
		Okv conf = Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras);
		if (isOk(orderColumns)) {
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, JBoltArrayUtil.from(orderColumns, KEY_COMMA));
		}
		if (isOk(orderTypes)) {
			conf.set(SQL_TPL_KEY_ORDER_TYPES, JBoltArrayUtil.from(orderTypes, KEY_COMMA));
		}
		return daoTemplate(SQL_TPL_COMMON_FIRST, conf).findFirst();
	}

	/**
	 * 随机得到符合条件的第一个
	 *
	 * @param paras
	 * @return
	 */
	public M getRandomOne(Okv paras) {
		return getRandomOne(paras, false);
	}

	/**
	 * 随机得到符合条件的第一个 oracle专用
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
//	public M getOracleRandomOne(Okv paras,boolean customCompare) {
//		int count=getCount(paras, customCompare);
//		if(count==0) {return null;}
//		if(count==1) {return findFirst(paras, customCompare);}
//		Okv conf=Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE,customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras).set(SQL_TPL_KEY_ORCLE_RANDOMROWNUM,RandomUtil.randomInt(1, count));
//		return daoTemplate(SQL_TPL_COMMON_FIRSTRAND, conf).findFirst();
//	}
	/**
	 * 随机得到符合条件的第一个
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public M getRandomOne(Okv paras, boolean customCompare) {
//		if(JBoltConfig.isOracle()) {
//			return getOracleRandomOne(paras,customCompare);
//		}
		Okv conf = Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras);
		switch (dbType()) {
		case DBType.MYSQL:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_MYSQL });
			break;
		case DBType.POSTGRESQL:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_POSTGRESQL });
			break;
		case DBType.SQLSERVER:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_NEWID_SQLSERVER });
			break;
		case DBType.DM:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_DM });
			break;
		case DBType.ORACLE:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_ORACLE });
			break;
		default:
			throw new RuntimeException("暂不支持数据库类型[" + dbType() + "]");
		}
		conf.set(SQL_TPL_KEY_ORDER_TYPES, new String[] { Sql.KEY_ASC_TRIMED });
		return daoTemplate(SQL_TPL_COMMON_FIRSTRAND, conf).findFirst();
	}

	/**
	 * 根据条件删除数据
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public Ret deleteBy(Okv paras, boolean customCompare) {
		if (JBoltConfig.DEMO_MODE) {
			return fail(JBoltMsg.DEMO_MODE_CAN_NOT_DELETE);
		}
		dbTemplate(SQL_TPL_COMMON_DELETE,
				Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras)).delete();
		return SUCCESS;
	}

	/**
	 * 根据条件删除数据
	 *
	 * @param paras
	 * @return
	 */
	public Ret deleteBy(Okv paras) {
		return deleteBy(paras, false);
	}

	/**
	 * 切换Boolean类型字段
	 *
	 * @param id      需要切换的数据ID
	 * @param columns 需要切换的字段列表
	 * @return
	 */
	public Ret toggleBoolean(Object id, String... columns) {
		return toggleBoolean(id,null,columns);
	}
	/**
	 * 切换Boolean类型字段值
	 *
	 * @param id      需要切换的数据ID
	 * @param kv      额外传入的参数 用于 toggleExtra里用
	 * @param columns 需要切换的字段列表
	 * @return
	 */
	public Ret toggleBoolean(Object id,Kv kv, String... columns) {
		if (notOk(id)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		M model = findById(id);
		if (model == null) {
			return fail(JBoltMsg.DATA_NOT_EXIST);
		}
		Boolean value;
		for (String column : columns) {
			if (!dao().hasColumn(column)) {
				return fail("字段【" + column + "】不存在");
			}
			String msg = checkCanToggle(model, column, kv);
			if (StrKit.notBlank(msg)) {
				return fail(msg);
			}

			value = model.getBoolean(column);
			model.set(column, (value == null ? true : !value));
			//update之前 切换一个之后执行
			String beforeMsg = beforeToggleBoolean(model,column,kv);
			if (StrKit.notBlank(beforeMsg)) {
				return fail(beforeMsg);
			}
		}
		//update之前 执行
		String beforeMsg = beforeToggleBoolean(model,kv);
		if (StrKit.notBlank(beforeMsg)) {
			return fail(beforeMsg);
		}
		//update执行
		boolean success = model.update();
		if (success) {
			// 处理完指定这个字段 还需要额外处理什么？
			for (String column : columns) {
				String msg = afterToggleBoolean(model, column, kv);
				if (StrKit.notBlank(msg)) {
					return fail(msg);
				}
			}
		}
		return success ? success(model, JBoltMsg.SUCCESS) : fail();
	}

	/**
	 * 常用的得到列表数据数量 自定义参数compare
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public int getCount(Okv paras, boolean customCompare) {
		return dbTemplate(SQL_TPL_COMMON_COUNT,
				Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras)).queryInt();
	}

	/**
	 * 查询数量
	 *
	 * @param sql
	 * @return
	 */
	public int getCount(Sql sql) {
		if (!sql.isQueryCount()) {
			sql.count();
		}
		return queryInt(sql);
	}

	/**
	 * 常用的得到列表数据数量
	 *
	 * @param paras
	 * @return
	 */
	public int getCount(Okv paras) {
		return getCount(paras, false);
	}

	/**
	 * 得到新数据的排序Rank值 默认从1开始 不带任何查询条件
	 *
	 * @return
	 */
	public int getNextSortRank() {
		return getNextSortRank(null, false);
	}

	/**
	 * 指定条件为pid的值 判断
	 *
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public int getNextSortRankByPid(String pidName, Object pidValue) {
		return getNextSortRank(Okv.by(pidName, pidValue == null ? 0 : pidValue), false);
	}

	/**
	 * 指定条件为pid的值 判断
	 *
	 * @param pidValue
	 * @return
	 */
	public int getNextSortRankByPid(Object pidValue) {
		return getNextSortRankByPid(PID, pidValue);
	}

	/**
	 * 得到新数据的排序Rank值 从0开始 不带任何查询条件
	 *
	 * @return
	 */
	public int getNextRankFromZero() {
		return getNextSortRank(null, true);
	}

	/**
	 * 得到新数据的排序Rank值 从0开始 带查询条件
	 *
	 * @param paras
	 * @return
	 */
	public int getNextRankFromZero(Okv paras) {
		return getNextSortRank(paras, true);
	}

	/**
	 * 得到新数据的排序Rank值 自带简单条件查询默认从1开始
	 *
	 * @param paras
	 * @return
	 */
	public int getNextSortRank(Okv paras) {
		return getNextSortRank(paras, false);
	}

	/**
	 * 得到新数据的排序Rank值 自带简单条件查询 可以自定义是否从零开始
	 *
	 * @param paras
	 * @param fromZero
	 * @return
	 */
	public int getNextSortRank(Okv paras, boolean fromZero) {
		int count = getCount(paras);
		if (fromZero) {
			return count;
		}
		return count + 1;
	}

	/**
	 * 得到新数据的排序Rank值 自带简单条件查询 可以自定义是否从零开始 条件可定制版
	 *
	 * @param paras
	 * @param customCompare
	 * @param fromZero
	 * @return
	 */
	public int getNextSortRank(Okv paras, Boolean customCompare, boolean fromZero) {
		int count = getCount(paras, customCompare);
		if (fromZero) {
			return count;
		}
		return count + 1;
	}

	/**
	 * 常用的得到列表数据数量
	 *
	 * @return
	 */
	public int getCount() {
		return getCount(null, false);
	}

	/**
	 * 更新同级删除数据之后数据的排序
	 *
	 * @param sortRank
	 */
	protected void updateSortRankAfterDelete(Integer sortRank) {
		updateSortRankAfterDelete(table(), sortRank);
	}

	/**
	 * 更新同级调整父节点数据之后数据的排序
	 *
	 * @param sortRank
	 */
	protected void updateSortRankAfterChangeParentNode(Integer sortRank) {
		updateSortRankAfterDelete(table(), sortRank);
	}

	/**
	 * 更新同级调整父节点数据之后数据的排序
	 *
	 * @param params
	 * @param sortRank
	 */
	protected void updateSortRankAfterChangeParentNode(Okv params, Integer sortRank) {
		updateSortRankAfterDelete(table(), params, sortRank);
	}

	/**
	 * 更新同级删除数据之后数据的排序
	 *
	 * @param params
	 * @param sortRank
	 */
	protected void updateSortRankAfterDelete(Okv params, Integer sortRank) {
		updateSortRankAfterDelete(table(), params, sortRank);
	}

	/**
	 * 删除关联子数据
	 *
	 * @param pid
	 * @return
	 */
	protected void deleteByPid(Object pid) {
		List<M> ms = getListByPid(pid);
		if (isOk(ms)) {
			// 还有子数据 就继续往下找 直到叶子节点
			for (M m : ms) {
				m.delete();
				// 继续根据ID去删除
				deleteByPid(m._getIdValue());
			}
		}
	}

	/**
	 * 根据PID获取子数据
	 *
	 * @param pid
	 * @return
	 */
	public List<M> getListByPid(Object pid) {
		return getCommonList(Okv.by(PID, pid));
	}

	/**
	 * 检测判断表中是否存在一个指定字段是null的数据
	 *
	 * @param columnName
	 * @return
	 */
	public boolean existsColumnIsNull(String columnName) {
		columnName = columnName.toLowerCase().trim();
		Sql sql = selectSql().selectId().isNull(columnName).first();
		Object existId = queryColumn(sql);
		return isOk(existId);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段 关键词为空的时候 返回空list
	 *
	 * @param keywords     关键词
	 * @param limitCount   返回个数
	 * @param matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @return
	 */
	public List<M> getAutocompleteList(String keywords, Integer limitCount, String matchColumns) {
		return getAutocompleteList(keywords, limitCount, false, null, matchColumns);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段 关键词为空的时候 返回空list
	 *
	 * @param keywords      关键词
	 * @param limitCount    返回个数
	 * @param returnColumns 查询字段 可以是* 也可以是 id,name这种逗号隔开
	 * @param matchColumns  关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @return
	 */
	public List<M> getAutocompleteList(String keywords, Integer limitCount, String returnColumns, String matchColumns) {
		return getAutocompleteList(keywords, limitCount, false, Sql.KEY_STAR, matchColumns);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段 关键词为空的时候 返回空list
	 *
	 * @param keywords     关键词
	 * @param limitCount   返回个数
	 * @param matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @param whereSql     额外条件sql
	 * @return
	 */
	public List<M> getAutocompleteList(String keywords, Integer limitCount, String matchColumns, Sql whereSql) {
		return getAutocompleteList(keywords, limitCount, false, Sql.KEY_STAR, matchColumns, whereSql);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段
	 *
	 * @param keywords     关键词
	 * @param limitCount   返回个数
	 * @param always       当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @param matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @return
	 */
	public List<M> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String matchColumns) {
		return getAutocompleteList(keywords, limitCount, always, null, matchColumns);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段
	 *
	 * @param keywords     关键词
	 * @param limitCount   返回个数
	 * @param always       当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @param matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @param whereSql     额外条件sql
	 * @return
	 */
	public List<M> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String matchColumns,
			Sql whereSql) {
		return getAutocompleteList(keywords, limitCount, always, Sql.KEY_STAR, matchColumns, whereSql);
	}

	/**
	 * 关键词查询指定返回个数的数据
	 *
	 * @param keywords      关键词
	 * @param limitCount    返回个数
	 * @param always        当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @param returnColumns 查询字段 可以是* 也可以是 id,name这种逗号隔开
	 * @param matchColumns  关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @return
	 */
	public List<M> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String returnColumns,
			String matchColumns) {
		return getAutocompleteList(keywords, limitCount, always, returnColumns, matchColumns, null);
	}

	/**
	 * 关键词查询指定返回个数的数据
	 *
	 * @param keywords      关键词
	 * @param limitCount    返回个数
	 * @param always        当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @param returnColumns 查询字段 可以是* 也可以是 id,name这种逗号隔开
	 * @param matchColumns  关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @param whereSql      额外条件
	 * @return
	 */
	public List<M> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String returnColumns,
			String matchColumns, Sql whereSql) {
		if ((notOk(keywords) && (always == null || always == false)) || notOk(matchColumns)) {
			return Collections.emptyList();
		}
		if (whereSql == null) {
			whereSql = selectSql();
		}else{
			whereSql.fromIfBlank(table());
		}
		whereSql.select(returnColumns).firstPage(limitCount);
		if(!whereSql.hasOrderColumns()){
			whereSql.orderByIdAscIfPgSql();
		}
		// 如果关键词为空 默认是返回空数据
		// 但是如果指定了关键词是空 就按照指定个数返回数据的话
		if (notOk(keywords) && always != null && always == true) {
			return find(whereSql);
		}

		String[] columns = JBoltArrayUtil.from(matchColumns, KEY_COMMA);
		if (columns.length == 0) {
			return Collections.emptyList();
		}
		int size = columns.length;
		whereSql.bracketLeft();
		keywords = keywords.trim();
		for (int i = 0; i < size; i++) {
			whereSql.like(columns[i], keywords);
			if (i < size - 1) {
				whereSql.or();
			}
		}
		whereSql.bracketRight();
		return find(whereSql);
	}

	/**
	 * 快速获取sql 默认是select sql
	 *
	 * @return
	 */
	public Sql selectSql() {
		return Sql.me(dbType()).select().from(table()).schema(schema());
	}

	/**
	 * 快速获取sql 使用jboltTableMenuFilter初始化
	 *
	 * @param jboltTableMenuFilter
	 * @param matchColumns
	 * @return
	 */
	public Sql selectSql(JBoltTableMenuFilter jboltTableMenuFilter, String... matchColumns) {
		Sql sql = selectSql();
		processJBoltTableMenuFilterSql(sql, jboltTableMenuFilter, matchColumns);
		return sql;
	}

	/**
	 * 快速获取sql 使用jboltTableMenuFilter初始化
	 *
	 * @param jboltTableMenuFilter
	 * @param mainTableAsName
	 * @param matchColumns
	 * @return
	 */
	public Sql selectSql(JBoltTableMenuFilter jboltTableMenuFilter, String mainTableAsName, String... matchColumns) {
		Sql sql = selectSql().from(table(), mainTableAsName);
		processJBoltTableMenuFilterSql(sql, jboltTableMenuFilter, matchColumns);
		return sql;
	}

	/**
	 * 快速获取update sql
	 *
	 * @return
	 */
	public Sql updateSql() {
		return Sql.me(dbType()).update(table()).schema(schema());
	}

	/**
	 * 快速获取delete sql
	 *
	 * @return
	 */
	public Sql deleteSql() {
		return Sql.me(dbType()).delete().from(table()).schema(schema());
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @return
	 */
	public List<M> find(Sql sql) {
		if (sql.isPrepared()) {
			return find(sql.toSql(), sql.getWhereValues());
		}
		return find(sql.toSql());
	}

	/**
	 * 使用Sql.java执行查询
	 *
	 * @param sql
	 * @return
	 */
	public List<Record> findRecord(Sql sql) {
		return findRecord(sql, false);
	}

	/**
	 * 使用Sql.java执行查询
	 *
	 * @param sql
	 * @param columnToCamelCase 是否转为驼峰key
	 * @return
	 */
	public List<Record> findRecord(Sql sql, boolean columnToCamelCase) {
		if (sql.isPrepared()) {
			return findRecord(sql.toSql(), columnToCamelCase, sql.getWhereValues());
		}
		return findRecord(sql.toSql(), columnToCamelCase);
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @param paras
	 * @return
	 */
	public List<M> find(String sql, Object... paras) {
		return dao().find(sql, paras);
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @param columnToCamelCase
	 * @param paras
	 * @return
	 */
	public List<Record> findRecord(String sql, boolean columnToCamelCase, Object... paras) {
		List<Record> records = Db.use(dataSourceConfigName()).find(sql, paras);
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(records);
		}
		return records;
	}

	/**
	 * 通过关键词查询数据List
	 *
	 * @param keywords
	 * @param orderColumn
	 * @param orderType
	 * @param matchColumns
	 * @return
	 */
	public List<M> getCommonListByKeywords(String keywords, String orderColumn, String orderType, String matchColumns) {
		return getCommonListByKeywords(keywords, Sql.KEY_STAR, orderColumn, orderType, matchColumns, null);
	}

	/**
	 * 通过关键词查询数据LIst 默认倒序
	 *
	 * @param keywords
	 * @param matchColumns
	 * @return
	 */
	public List<M> getCommonListByKeywords(String keywords, String matchColumns) {
		return getCommonListByKeywords(keywords, ID, matchColumns);
	}

	/**
	 * 通过关键词查询数据List 默认倒序
	 *
	 * @param keywords
	 * @param orderColumn
	 * @param matchColumns
	 * @return
	 */
	public List<M> getCommonListByKeywords(String keywords, String orderColumn, String matchColumns) {
		return getCommonListByKeywords(keywords, orderColumn, Sql.KEY_DESC_TRIMED, matchColumns);
	}

	/**
	 * 通过关键词查询数据List
	 *
	 * @param keywords
	 * @param orderColumn
	 * @param orderType
	 * @param matchColumns
	 * @param otherParas
	 * @return
	 */
	public List<M> getCommonListByKeywords(String keywords, String orderColumn, String orderType, String matchColumns,
			Okv otherParas) {
		return getCommonListByKeywords(keywords, Sql.KEY_STAR, orderColumn, orderType, matchColumns, otherParas);
	}

	/**
	 * 通过关键词查询数据List
	 *
	 * @param keywords
	 * @param returnColumns
	 * @param orderColumn
	 * @param orderType
	 * @param matchColumns
	 * @return
	 */
	public List<M> getCommonListByKeywords(String keywords, String returnColumns, String orderColumn, String orderType,
			String matchColumns) {
		return getCommonListByKeywords(keywords, returnColumns, orderColumn, orderType, matchColumns, null);
	}

	/**
	 * 通过关键词查询数据List底层封装
	 *
	 * @param keywords
	 * @param returnColumns
	 * @param orderColumn
	 * @param orderType
	 * @param matchColumns
	 * @param otherParas
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<M> getCommonListByKeywords(String keywords, String returnColumns, String orderColumn, String orderType,
			String matchColumns, Okv otherParas) {
		if (notOk(matchColumns)) {
			return Collections.emptyList();
		}
		Sql sql = selectSql().select(returnColumns);
		if (isOk(orderColumn) && isOk(orderType)) {
			sql.orderBy(orderColumn, Sql.KEY_DESC_TRIMED.equals(orderType));
		} else {
			sql.orderByIdAscIfPgSql();
		}
		if (otherParas != null && otherParas.size() > 0) {
			sql.bracketLeft();
			otherParas.forEach((key, value) -> sql.eq(key.toString(), value));
			sql.bracketRight();
		}
		// 如果没有给keywords字段
		if (notOk(keywords)) {
			return find(sql);
		}
		// 如果给了Keywords
		String[] columns = JBoltArrayUtil.from(matchColumns, KEY_COMMA);
		if (columns.length == 0) {
			return Collections.emptyList();
		}
		int size = columns.length;
		sql.bracketLeft();
		keywords = keywords.trim();
		for (int i = 0; i < size; i++) {
			sql.like(columns[i], keywords);
			if (i < size - 1) {
				sql.or();
			}
		}
		sql.bracketRight();

		return find(sql);

	}

	/**
	 * 根据主键删除缓存
	 * @param ids
	 */
	public void deleteCacheById(Object... ids) {
		dao().deleteCacheById(ids);
	}

	/**
	 * 根据指定列值删除缓存
	 *
	 * @param columnValue
	 */
	public void deleteCacheByKey(String columnValue) {
		dao().deleteCacheByKey(columnValue, null);
	}

	/**
	 * 根据指定列值删除缓存
	 *
	 * @param columnValue
	 * @param bindColumnValue
	 */
	public void deleteCacheByKey(String columnValue, Object bindColumnValue) {
		dao().deleteCacheByKey(columnValue, bindColumnValue);
	}

	/**
	 * 根据指定列值获得缓存
	 *
	 * @param columnValue
	 * @return
	 */
	public M getCacheByKey(String columnValue) {
		return dao().loadCacheByKey(columnValue);
	}

	/**
	 * 根据指定列值和绑定列值获得缓存
	 *
	 * @param columnValue
	 * @param bindColumnValue
	 * @return
	 */
	public M getCacheByKey(String columnValue, Object bindColumnValue) {
		return dao().loadCacheByKey(columnValue, bindColumnValue);
	}

	/**
	 * 转换为Model tree
	 *
	 * @param allList
	 * @param idColumn
	 * @param pidColumn
	 * @param checkIsParentFunc
	 * @return
	 */
	public List<M> convertToModelTree(List<M> allList, String idColumn, String pidColumn,
			TreeCheckIsParentNode<M> checkIsParentFunc) {
		if (notOk(allList)) {
			return allList;
		}
		List<M> parents = new ArrayList<M>();
		M m;
		for (int i = 0; i < allList.size(); i++) {
			m = allList.get(i);
			m.putEachLevel(1);
			if (checkIsParentFunc.isParent(m)) {
				parents.add(m);
				allList.remove(m);
				i--;
			}
		}
		if (parents.size() > 0 && allList.size() > 0) {
			processTreeNodes(allList, parents, idColumn, pidColumn);
		}
		return parents;
	}

	/**
	 * 处理tree节点
	 *
	 * @param allList
	 * @param parents
	 * @param idColumn
	 * @param pidColumn
	 */
	private void processTreeNodes(List<M> allList, List<M> parents, String idColumn, String pidColumn) {
		JBoltListMap<String, M> map = new JBoltListMap<String, M>();
		for (M m : allList) {
			map.addItem("p_" + m.get(pidColumn), m);
		}
		for (M p : parents) {
			processTreeSubNodes(map, p, idColumn);
		}

	}

	/**
	 * 递归处理tree子节点
	 *
	 * @param map
	 * @param p
	 * @param idColumn
	 */
	private void processTreeSubNodes(JBoltListMap<String, M> map, M p, String idColumn) {
		List<M> items = map.get("p_" + p.get(idColumn));
		if (items != null && items.size() > 0) {
			for (M item : items) {
				item.processEachLevelByParentLevel(p.getEachLevel());
				processTreeSubNodes(map, item, idColumn);
			}
		}
		p.putItems(items);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas     数据源
	 * @param selectedId 选中节点ID
	 * @param openLevel -1全部 0 不动 >0指定层级层级
	 * @param orderColumn 排序字段
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel,String orderColumn) {
		return convertJsTree(datas, selectedId, openLevel, null, orderColumn,false);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas          数据源
	 * @param selectedId     选中节点ID
	 * @param openLevel      -1全部 0 不动 >0指定层级模式
	 * @param orderColumn 排序字段
	 * @param needOriginData 是否需要保留原始Data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel,String orderColumn, boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, null,orderColumn, needOriginData);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas      数据源
	 * @param selectedId 选中哪一个节点
	 * @param openLevel  -1全部 0 不动 >0指定层级层级
	 * @param keyColumn  多级下 key字段名字
	 * @param orderColumn 排序字段
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,String orderColumn) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, orderColumn, null,false);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级层级
	 * @param keyColumn      多级下 key字段名字
	 * @param orderColumn    排序字段
	 * @param needOriginData 是否需要保留原始data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,String orderColumn,
			boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, orderColumn,null, needOriginData);
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas      数据源
	 * @param selectedId 选中哪一个节点
	 * @param openLevel  -1全部 0 不动 >0指定层级层级
	 * @param keyColumn  多级下 key字段名字
	 * @param orderColumn 排序字段
	 * @param rootName   根节点名字
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,
			String orderColumn,String rootName) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, NAME, orderColumn,rootName, false);
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级层级
	 * @param keyColumn      多级下 key字段名字
	 * @param orderColumn    排序字段
	 * @param rootName       根节点名字
	 * @param needOriginData 是否需要保留原始data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,String orderColumn,
			String rootName, boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, NAME, orderColumn, rootName, needOriginData);
	}
	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级层级
	 * @param keyColumn      多级下 key字段名字
	 * @param orderColumn    排序字段
	 * @param rootName       根节点名字
	 * @param needOriginData 是否需要保留原始data
	 * @param checkIsParentFunc 筛选parents
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,String orderColumn,
										  String rootName, boolean needOriginData,TreeCheckIsParentNode<M> checkIsParentFunc) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn,ID,PID,NAME,ENABLE,orderColumn,rootName,needOriginData,checkIsParentFunc);
	}
	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级层级
	 * @param keyColumn      多级下 key字段名字
	 * @param textColumn     显示文本字段
	 * @param orderColumn     排序字段
	 * @param rootName       根节点名字
	 * @param needOriginData 是否需要保留原始data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,
			String textColumn, String orderColumn,String rootName, boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, textColumn, ENABLE, orderColumn,rootName, needOriginData);
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级层级
	 * @param keyColumn      多级下 key字段名字
	 * @param textColumn     显示文本字段
	 * @param enableColumn   启用禁用字段
	 * @param orderColumn    排序字段
	 * @param rootName       根节点名字
	 * @param needOriginData 是否需要保留原始Data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,
			String textColumn, String enableColumn,String orderColumn, String rootName, boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, ID, PID, textColumn, enableColumn,orderColumn, rootName,
				needOriginData, null);
	}

	private void processJsTreeType(List<M> jstreeMs) {
		if (isOk(jstreeMs)) {
			List<M> items;
			for (M m : jstreeMs) {
				items = m.getItems();
				if (isOk(items)) {
					m.put(KEY_JS_TREE_TYPE, KEY_PARENT);
					processJsTreeType(items);
				} else {
					m.put(KEY_JS_TREE_TYPE, KEY_NODE);
				}
			}
		}
	}

	private String[] getTreeColumnKeys(M m, String idColumn, String pidColumn) {
		List<String> keys = new ArrayList<String>();
		keys.add(m.getStr(idColumn));
		Object pid = m.getStr(pidColumn);
		if (pid != null && !pid.toString().equals(ZERO_STR)) {
			processTreeColumnKeys(keys, pid, idColumn, pidColumn);
		}
		return keys.toArray(new String[0]);
	}

	private void processTreeColumnKeys(List<String> keys, Object id, String idColumn, String pidColumn) {
		if (id == null || id.toString().equals(ZERO_STR)) {
			return;
		}
		M m = findById(id);
		if (m == null) {
			return;
		}
		// pid有值 需要加入keys
		keys.add(0, id.toString());
		Object pid = m.getStr(pidColumn);
		if (pid != null && !pid.toString().equals(ZERO_STR)) {
			// pid有值 需要加入keys
			processTreeColumnKeys(keys, pid, idColumn, pidColumn);
		}
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级
	 * @param keyColumn      多级下 key字段名字
	 * @param idColumn       节点 标识字段
	 * @param pidColumn      节点父标识字段
	 * @param textColumn     显示文本字段
	 * @param enableColumn   启用禁用字段
	 * @param orderColumn      排序字段
	 * @param rootName       跟节点名字
	 * @param needOriginData 是否需要保留原始data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<M> datas, Object selectedId, int openLevel, String keyColumn,
			String idColumn, String pidColumn, String textColumn, String enableColumn,String orderColumn, String rootName,
			boolean needOriginData, TreeCheckIsParentNode<M> checkIsParentFunc) {
		List<JsTreeBean> treeBeans = new ArrayList<JsTreeBean>();
		boolean hasRoot = isOk(rootName);

		if (datas!=null&& !datas.isEmpty()) {
			String[] keys = null;
			if (isOk(selectedId)) {
				M m = findById(selectedId);
				if (m != null) {
					if (isOk(keyColumn)) {
						String key = m.get(keyColumn);
						if (isOk(key)) {
							keys = JBoltArrayUtil.from(key, KEY_UNDERLINE);
						}
					} else {
						keys = getTreeColumnKeys(m, idColumn, pidColumn);
					}
				}
			}

			List<M> jstreeMs;
			if (checkIsParentFunc != null) {
				jstreeMs = convertToModelTree(datas, idColumn, pidColumn, (p) -> checkIsParentFunc.isParent(p));
			} else {
				final Set<String> pkey = new HashSet<>();
				jstreeMs = convertToModelTree(datas, idColumn, pidColumn, (p) -> {
					//如果pid值没有 就是parent
					String pidValue = p.getStr(pidColumn);
					//如果ID值不在里面
					String idValue = p.getStr(idColumn);
					if(pidValue == null || pidValue.trim().isEmpty() || pidValue.equals(ZERO_STR)){
						pkey.add("pkey_0");
						pkey.add("pkey_"+idValue);
						return true;
					}
					/*//如果PID值存在
					if(pkey.contains("pkey_"+pv.toString())){
						pkey.add("pkey_"+pv.toString());
						pkey.add("pkey_"+idValue.toString());
						return false;
					}
					if(idValue != null && !idValue.toString().equals(ZERO_STR)){
						pkey.add("pkey_"+idValue.toString());
//						if(hasRoot){
//							p.set(pidColumn,0);
//						}
						return true;
					}*/
					return false;
				});
				pkey.clear();
			}

			processJsTreeType(jstreeMs);

			processJsTree(treeBeans, jstreeMs, openLevel, 1, idColumn, pidColumn, textColumn, enableColumn,orderColumn, keys,
					selectedId, hasRoot, needOriginData);
		}

		if (hasRoot) {
			treeBeans.add(0,
					new JsTreeBean(0, 0, rootName, openLevel != 0, openLevel == 0 ? KEY_ROOT : KEY_ROOT_OPENED, true));
		}
		return treeBeans;
	}

	private void processJsTree(List<JsTreeBean> treeBeans, List<M> jstreeMs, int openLevel, int currentLevel,
			String idColumn, String pidColumn, String textColumn, String enableColumn, String orderColumn,String[] keys, Object selectedId,
			boolean hasRoot, boolean needOriginData) {
		if (notOk(jstreeMs)) {
			return;
		}
		if(orderColumn!=null && orderColumn.trim().length()==0){
			orderColumn = null;
		}
		if(orderColumn == null){
			if(dao().hasColumn(SORT_RANK)){
				orderColumn = SORT_RANK;
			}else if(dao().hasColumn(SORT_RANK.toUpperCase())){
				orderColumn = SORT_RANK.toUpperCase();
			}
		}
		boolean opened = false;
		boolean keysIsOk = isOk(keys);
		Object idValue;
//		Object pidValue;
		String type;
		Integer order = 0;
		int index = 0;
		JsTreeBean treeBean;
		for (M m : jstreeMs) {
			opened = openLevel == -1;
			boolean selected = false;

//			pidValue=m.get(pidColumn);
			if (!opened && currentLevel <= openLevel) {
				opened = true;
			}
			idValue = m.get(idColumn);
			if (keysIsOk && idValue != null && !idValue.toString().equals(ZERO_STR)
					&& JBoltArrayUtil.contains(keys, idValue.toString())) {
				opened = true;
			}
			if (selectedId != null) {
				assert idValue != null;
				if (idValue.toString().equals(selectedId.toString())) {
					selected = true;
					opened = true;
				}
			}

			type = m.get(KEY_JS_TREE_TYPE);
			if (notOk(type)) {
				type = KEY_PARENT;
			}
			if (!KEY_NODE.equals(type) && opened) {
				type = KEY_PARENT_OPENED;
			}
			treeBean = convertToJsTreeBean(m, opened, selected, type, idColumn, pidColumn, textColumn, enableColumn,
					hasRoot, needOriginData);
			if(orderColumn != null){
				order = m.getInt(orderColumn);
				if(order==null){
					treeBean.setOrder(0);
				}else{
					treeBean.setOrder(order);
				}
			}else{
				treeBean.setOrder(index);
				index++;
			}
			treeBeans.add(treeBean);
			processJsTree(treeBeans, m.getItems(), openLevel, (currentLevel + 1), idColumn, pidColumn, textColumn,
					enableColumn,orderColumn, keys, selectedId, hasRoot, needOriginData);
		}

	}

	public JsTreeBean convertToJsTreeBean(M m, boolean opened, boolean selected, String type, String idColumn,
			String pidColumn, String textColumn, String enableColumn, boolean hasRoot, boolean needOriginData) {
		if (needOriginData) {
			return new JsTreeBean(m.get(idColumn), m.get(pidColumn), getEnableName(m, textColumn, enableColumn), opened,
					selected, type, hasRoot).setData(m);
		}
		return new JsTreeBean(m.get(idColumn), m.get(pidColumn), getEnableName(m, textColumn, enableColumn), opened,
				selected, type, hasRoot);
	}

	/**
	 * 树形结构上单独使用的
	 *
	 * @return
	 */
	private String getEnableName(M m, String textColumn, String enableColumn) {
		String name = null;
		if(textColumn.contains(",")){
			String[] arr = JBoltArrayUtil.from3(textColumn);
			if(arr!=null && arr.length>0){
				name ="";
				String temp;
				for(String a:arr){
					temp =  m.getStr(a);
					if("name".equalsIgnoreCase(a)||"text".equalsIgnoreCase(a)||"title".equalsIgnoreCase(a)){
						name = name + StrKit.defaultIfBlank(temp,"-");
					}else{
						name = name + "["+StrKit.defaultIfBlank(temp,"-")+"]";
					}
				}
			}
		}else{
			name = m.get(textColumn);
		}
		//如果表里没有enableColumn指定的字段 就直接返回不处理了
		if(!m.hasColumn(enableColumn)){
			return name;
		}
		//如果有 在处理
		Boolean enable = m.getBoolean(enableColumn);
		return (enable != null && enable) ? name : (name + "[<span class='text-danger'>禁用</span>]");
	}

	/**
	 * 更新指定ID数据的指定字段的值
	 *
	 * @param id
	 * @param column
	 * @param value
	 * @return
	 */
	public Ret updateColumn(Object id, String column, Object value) {
		M m = findById(id);
		if (m == null) {
			return fail(JBoltMsg.DATA_NOT_EXIST);
		}
		// 如果存在就可以操作了
		m.set(column, value);
		boolean success = m.update();
		if (success) {
			return successWithData(m);
		}
		return fail();
	}

	/**
	 * 更新字段值
	 *
	 * @param m
	 * @return
	 */
	public Ret updateOneColumn(M m) {
		if (m == null) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		String primaryKey = m._getPrimaryKey();
		if (!m.hasColumn(primaryKey)) {
			return fail(JBoltMsg.PARAM_ERROR + ",未设置主键参数");
		}
		Object idValue = m._getIdValue();
		if (idValue == null) {
			return fail(JBoltMsg.PARAM_ERROR + ",未设置主键值");
		}
		String[] attrNames = m._getAttrNames();
		if (attrNames.length != 2) {
			return fail(JBoltMsg.PARAM_ERROR + ",除了主键还需要设置更新字段与值");
		}
		String column = null;
		Object value = null;
		for (String attr : attrNames) {
			if (primaryKey.equalsIgnoreCase(attr)) {
				continue;
			}
			column = attr;
			value = m.get(column);
		}
		return updateColumn(idValue, column, value);
	}

	/**
	 * 清空缓存 自定义检索范围sql
	 */
	public void clearCacheBySql(Sql sql) {
		dao().each(m -> {
			m.clearCache();
			return true;
		}, sql.toSql());
	}

	/**
	 * 清空缓存
	 */
	public void clearCache() {
		dao().each(config -> {
			config.clearCache();
			return true;
		}, selectSql().toSql());
	}

	/**
	 * 得到所有的一级 表结构为树结构的使用
	 *
	 * @param pidName
	 * @param parentPidValue
	 * @param sortColumnName
	 * @param desc
	 * @return
	 */
	public List<M> getAllParents(String pidName, Object parentPidValue, String sortColumnName, boolean desc) {
		return getCommonList(Okv.by(pidName, parentPidValue), sortColumnName,
				desc ? Sql.KEY_DESC_TRIMED : Sql.KEY_ASC_TRIMED);
	}

	/**
	 * 得到所有的一级 表结构为树结构的使用
	 *
	 * @param pidName
	 * @param parentPidValue
	 * @param sortColumnName
	 * @return
	 */
	public List<M> getAllParents(String pidName, Object parentPidValue, String sortColumnName) {
		return getAllParents(pidName, parentPidValue, sortColumnName, false);
	}

	/**
	 * 得到指定pid下的所有下一级parent
	 *
	 * @param parentPidValue
	 * @return
	 */
	public List<M> getAllParents(Object parentPidValue) {
		return getAllParents(PID, parentPidValue, SORT_RANK);
	}

	/**
	 * 得到所有的一级parent节点
	 *
	 * @return
	 */
	public List<M> getAllParents() {
		return getAllParents(0);
	}

	/**
	 * 得到指定pid的一级
	 *
	 * @param pidName
	 * @param pidValue
	 * @param sortColumnName
	 * @return
	 */
	public List<M> getSons(String pidName, Object pidValue, String sortColumnName) {
		if(notOk(pidValue)){
			return null;
		}
		return getCommonList(Okv.by(pidName, pidValue), sortColumnName);
	}

	/**
	 * 得到指定pid的一级 按照sortRank字段排序
	 *
	 * @param pidValue
	 * @return
	 */
	public List<M> getSonsOrderBySortRank(Object pidValue) {
		return getSons(PID, pidValue, SORT_RANK);
	}

	/**
	 * 得到指定pid的一级 不带字段排序
	 *
	 * @param pidValue
	 * @return
	 */
	public List<M> getSons(Object pidValue) {
		return getSons(PID, pidValue, null);
	}

	/**
	 * 得到所有的一级数据 按照ID 正序排序
	 *
	 * @return
	 */
	public List<M> getAllParentsOrderById() {
		return getAllParents(PID, 0, ID);
	}

	/**
	 * 得到所有的一级数据 按照sort_rank 正序排序
	 *
	 * @return
	 */
	public List<M> getAllParentsOrderBySortRank() {
		return getAllParents(PID, 0, SORT_RANK);
	}

	/**
	 * 得到所有一级数据 按照sort_rank 正序排序
	 * @param enable
	 * @return
	 */
	public List<M> getAllParentsOrderBySortRank(Boolean enable) {
		if(enable == null){
			return getAllParentsOrderBySortRank();
		}
		return getCommonList(Okv.by(PID, ZERO).set(ENABLE,enable?TRUE:FALSE), SORT_RANK,Sql.KEY_ASC_TRIMED);

	}
	/**
	 * 得到所有enable=true 的一级数据 按照sort_rank 正序排序
	 *
	 * @return
	 */
	public List<M> getEnableSonsOrderBySortRank(Object parentPidValue) {
		return getCommonList(Okv.by(PID, parentPidValue).set(ENABLE,TRUE), SORT_RANK,Sql.KEY_ASC_TRIMED);
	}

	/**
	 * 得到所有enable=false 的一级数据 按照sort_rank 正序排序
	 *
	 * @return
	 */
	public List<M> getNotEnableSonsOrderBySortRank(Object parentPidValue) {
		return getCommonList(Okv.by(PID, parentPidValue).set(ENABLE,FALSE), SORT_RANK,Sql.KEY_ASC_TRIMED);
	}

	/**
	 * 得到指定pid的一级 按照ID 正序排序
	 *
	 * @param pidValue
	 * @return
	 */
	public List<M> getSonsOrderById(Object pidValue) {
		return getSons(PID, pidValue, ID);
	}

	/**
	 * 批量更新
	 *
	 * @param models
	 * @retun
	 */
	public int[] batchUpdate(List<M> models) {
		if (notOk(models)){
			return NONE_BATCH_RESULT;
		}
		int batchSize = Math.min(models.size(), 500);
		return Db.use(dataSourceConfigName()).batchUpdate(models, batchSize);
	}

	/**
	 * 批量更新
	 *
	 * @param models
	 * @param batchSize
	 * @return
	 */
	public int[] batchUpdate(List<M> models, int batchSize) {
		if (notOk(models)){
			return NONE_BATCH_RESULT;
		}
		return Db.use(dataSourceConfigName()).batchUpdate(models, batchSize);
	}

	/**
	 * 批量插入
	 *
	 * @param models
	 * @return
	 */
	public int[] batchSave(List<M> models) {
		if (notOk(models)){
			return NONE_BATCH_RESULT;
		}
		int batchSize = Math.min(models.size(), 500) ;
		return Db.use(dataSourceConfigName()).batchSave(models, batchSize);
	}

	/**
	 * 批量插入
	 *
	 * @param models
	 * @param batchSize
	 * @return
	 */
	public int[] batchSave(List<M> models, int batchSize) {
		if (notOk(models)){
			return NONE_BATCH_RESULT;
		}
		return Db.use(dataSourceConfigName()).batchSave(models, batchSize);
	}

	/**
	 * 批量保存
	 * @param records
	 */
	protected int[] batchSaveRecords(List<Record> records) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		int batchSize = Math.min(records.size(), 500) ;
		return Db.use(dataSourceConfigName()).batchSave(table(), records, batchSize);
	}
	/**
	 * 批量保存
	 * @param records
	 * @param batchSize
	 */
	protected int[] batchSaveRecords(List<Record> records,int batchSize) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		return Db.use(dataSourceConfigName()).batchSave(table(), records, batchSize);
	}
	/**
	 * 批量更新
	 * @param records
	 */
	protected int[] batchUpdateRecords(List<Record> records) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		int batchSize = Math.min(records.size(), 500) ;
		return Db.use(dataSourceConfigName()).batchUpdate(table(), records, batchSize);

	}
	/**
	 * 批量更新
	 *
	 * @param records
	 * @param batchSize
	 */
	protected int[] batchUpdateRecords(List<Record> records, int batchSize) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		return Db.use(dataSourceConfigName()).batchUpdate(table(), records, batchSize);
	}

	/**
	 * 有数据
	 *
	 * @return
	 */
	public boolean notEmpty() {
		int count = getCount();
		return count > 0;
	}

	/**
	 * 是否为空 没数据
	 *
	 * @return
	 */
	public boolean isEmpty() {
		int count = getCount();
		return count == 0;
	}

	/**
	 * 查询基于menuFilter
	 *
	 * @param jboltTableMenuFilter
	 * @param matchColumns
	 * @return
	 */
	public Page<M> paginateByJboltTableMenuFilter(JBoltTableMenuFilter jboltTableMenuFilter, String[] matchColumns) {
		return paginate(selectSql(jboltTableMenuFilter, matchColumns));
	}

	/**
	 * 切换禁用启用状态
	 *
	 * @param id
	 * @return
	 */
	public Ret toggleEnable(Object id) {
		return toggleBoolean(id, ENABLE);
	}

	/**
	 * 切换isDefault属性
	 *
	 * @param id
	 * @return
	 */
	public Ret toggleIsDefault(Object id) {
		return toggleBoolean(id, IS_DEFAULT);
	}

	/**
	 * 将指定排除数据之外的所有数据指定column转为false
	 *
	 * @param column
	 * @param excludedId
	 */
	public void changeAllColumnToFalseByExcludedId(String column, Object excludedId) {
		update(updateSql().set(column, FALSE).notEq(primaryKey(), excludedId));
	}

	/**
	 * 将指定排除数据之外的所有数据指定column转为true
	 *
	 * @param column
	 * @param excludedId
	 */
	public void changeAllColumnToTrueByExcludedId(String column, Object excludedId) {
		update(updateSql().set(column, TRUE).notEq(primaryKey(), excludedId));
	}

	/**
	 * 将所有数据的column数据转为false
	 */
	public void changeAllColumnToFalse(String column) {
		update(updateSql().set(column, FALSE));
	}

	/**
	 * 将所有数据的column数据转为true
	 */
	public void changeAllColumnToTrue(String column) {
		update(updateSql().set(column, TRUE));
	}

	/**
	 * 将指定ID数据的column数据转为false
	 */
	public void changeColumnFalseById(String column, Object id) {
		update(updateSql().set(column, FALSE).eq(primaryKey(), id));
	}

	/**
	 * 将指定ID数据的column数据转为true
	 */
	public void changeColumnTrueById(String column, Object id) {
		update(updateSql().set(column, TRUE).eq(primaryKey(), id));
	}

	/**
	 * 将现在ID不为指定ID的数据的column数据转为false
	 *
	 * @param column
	 * @param excludedId
	 */
	public void changeColumnFalseByExcludedId(String column, Object excludedId) {
		changeColumnFalse(column, excludedId, null);
	}

	/**
	 * 将现在ID不为指定ID的数据的column数据转为true
	 *
	 * @param column
	 * @param excludedId
	 */
	public void changeColumnTrueByExcludedId(String column, Object excludedId) {
		changeColumnTrue(column, excludedId, null);
	}

	/**
	 * 将现在ID不为指定ID的数据的column数据转为false
	 *
	 * @param column              指定列
	 * @param excludedId          排除ID
	 * @param otherUpdateWhereSql 额外条件sql
	 */
	public void changeColumnFalse(String column, Object excludedId, Sql otherUpdateWhereSql) {
		changeColumnBoolean(column, excludedId, otherUpdateWhereSql, false);
	}

	/**
	 * 将现在ID不为指定ID的数据的column数据转为true
	 *
	 * @param column              指定列
	 * @param excludedId          排除ID
	 * @param otherUpdateWhereSql 额外条件sql
	 */
	public void changeColumnTrue(String column, Object excludedId, Sql otherUpdateWhereSql) {
		changeColumnBoolean(column, excludedId, otherUpdateWhereSql, true);
	}

	/**
	 * 将现在ID不为指定ID的数据的column数据转为指定boolean值
	 *
	 * @param column              指定列
	 * @param excludedId          排除ID
	 * @param otherUpdateWhereSql 额外条件sql
	 */
	public void changeColumnBoolean(String column, Object excludedId, Sql otherUpdateWhereSql, boolean value) {
		Sql sql;
		if (otherUpdateWhereSql == null) {
			sql = updateSql().set(column, value ? TRUE : FALSE).eq(column, value ? FALSE : TRUE);
		} else {
			if (!otherUpdateWhereSql.isUpdate()) {
				otherUpdateWhereSql.update(table());
			}
			sql = otherUpdateWhereSql;
			sql.set(column, value ? TRUE : FALSE).eq(column, value ? FALSE : TRUE);
		}
		if(isOk(excludedId)){
			sql.notEq(primaryKey(),excludedId);
		}
		update(sql);
	}

	/**
	 * 检测是否可以存在指定column=true 排除指定ID
	 *
	 * @param column     指定column
	 * @param excludedId 排除指定ID
	 * @return
	 */
	public boolean checkColumnTrueExist(String column, Object excludedId) {
		return checkColumnBooleanExist(column, true, excludedId, null);
	}

	/**
	 * 检测是否可以存在指定column=false 排除指定ID
	 *
	 * @param column     指定column
	 * @param excludedId 排除指定ID
	 * @return
	 */
	public boolean checkColumnFalseExist(String column, Object excludedId) {
		return checkColumnBooleanExist(column, false, excludedId, null);
	}

	/**
	 * 检测是否可以存在指定column 值的数据
	 *
	 * @param column        列名
	 * @param value         boolean值
	 * @param excludedId    排除ID 不填就检查整个表
	 * @param otherWhereSql 满足其他条件 可不传
	 * @return
	 */
	public boolean checkColumnBooleanExist(String column, boolean value, Object excludedId, Sql otherWhereSql) {
		if (otherWhereSql == null) {
			if (isOk(excludedId)) {
				return exists(column, value ? TRUE : FALSE, excludedId);
			}
			return exists(column, value ? TRUE : FALSE);
		}

		if (isOk(excludedId)) {
			otherWhereSql.notEq(primaryKey(), excludedId);
		}
		return exists(otherWhereSql);
	}
	/**
	 * 异步jstree
	 * @param mlist
	 * @param isRoot
	 * @return
	 */
	public List<JsTreeBean> convertJsTreeForAsync(List<M> mlist,boolean isRoot) {
		return convertJsTreeForAsync(mlist,isRoot,ID,PID,NAME,false);
	}
	/**
	 * 异步jstree
	 * @param mlist
	 * @param isRoot
	 * @param needOriginData
	 * @return
	 */
	public List<JsTreeBean> convertJsTreeForAsync(List<M> mlist,boolean isRoot,boolean needOriginData) {
		return convertJsTreeForAsync(mlist,isRoot,ID,PID,NAME,needOriginData);
	}
	/**
	 * 异步jstree
	 * @param mlist
	 * @param isRoot
	 * @param nameColumn
	 * @param needOriginData
	 * @return
	 */
	public List<JsTreeBean> convertJsTreeForAsync(List<M> mlist,boolean isRoot,String nameColumn,boolean needOriginData) {
		return convertJsTreeForAsync(mlist,isRoot,ID,PID,nameColumn,needOriginData);
	}
	/**
	 * 异步jstree
	 * @param mlist
	 * @param isRoot
	 * @param idColumn
	 * @param pidColumn
	 * @param nameColumn
	 * @param needOriginData
	 * @return
	 */
	public List<JsTreeBean> convertJsTreeForAsync(List<M> mlist,boolean isRoot,String idColumn,String pidColumn,String nameColumn,boolean needOriginData) {
		List<JsTreeBean> jsTreeBeans=new ArrayList<JsTreeBean>();
		JsTreeBean jsTreeBean;
		boolean isParent=false;
		for(M m:mlist) {
			jsTreeBean=new JsTreeBean(m.get(idColumn),m.get(pidColumn), m.get(nameColumn), false);
			jsTreeBean.setState(new JsTreeStateBean(false));
			isParent=existsSon(pidColumn,m.get(idColumn));
			jsTreeBean.setChildren(isParent);
			if(isParent) {
				jsTreeBean.setType(isRoot?"root":"parent");
			}else {
				jsTreeBean.setType("node");
			}
			if(needOriginData){
				jsTreeBean.setData(m);
			}
			jsTreeBeans.add(jsTreeBean);
		}
		return jsTreeBeans;
	}

	public Class<? extends JBoltBaseModel> getModelClass() {
		return dao().getClass();
	}
}
